68

我正在使用 GestureDetector.SimpleOnGestureListener 的 onScroll 方法在画布上滚动大位图。当滚动结束时,我想重新绘制位图,以防用户想要进一步滚动......离开位图的边缘,但我看不到如何检测滚动何时结束(用户抬起手指从屏幕上)。

e2.getAction() 似乎总是返回值 2 所以这没有帮助。e2.getPressure 似乎返回相当恒定的值(大约 0.25),直到最后一次 onScroll 调用,此时压力似乎下降到大约 0.13。我想我可以检测到压力的降低,但这远非万无一失。

必须有更好的方法:有人可以帮忙吗?

4

14 回答 14

70

这是我解决问题的方法。希望这可以帮助。

// declare class member variables
private GestureDetector mGestureDetector;
private OnTouchListener mGestureListener;
private boolean mIsScrolling = false;


public void initGestureDetection() {
        // Gesture detection
    mGestureDetector = new GestureDetector(new SimpleOnGestureListener() {
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            handleDoubleTap(e);
            return true;
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            handleSingleTap(e);
            return true;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            // i'm only scrolling along the X axis
            mIsScrolling = true;                
            handleScroll(Math.round((e2.getX() - e1.getX())));
            return true;
        }

        @Override
        /**
         * Don't know why but we need to intercept this guy and return true so that the other gestures are handled.
         * https://code.google.com/p/android/issues/detail?id=8233
         */
        public boolean onDown(MotionEvent e) {
            Log.d("GestureDetector --> onDown");
            return true;
        }
    });

    mGestureListener = new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {

            if (mGestureDetector.onTouchEvent(event)) {
                return true;
            }

            if(event.getAction() == MotionEvent.ACTION_UP) {
                if(mIsScrolling ) {
                    Log.d("OnTouchListener --> onTouch ACTION_UP");
                    mIsScrolling  = false;
                    handleScrollFinished();
                };
            }

            return false;
        }
    };

    // attach the OnTouchListener to the image view
    mImageView.setOnTouchListener(mGestureListener);
}
于 2010-09-29T01:09:31.670 回答
4

你应该看看http://developer.android.com/reference/android/widget/Scroller.html。特别是这可能会有所帮助(按相关性排序):

isFinished();
computeScrollOffset();
getFinalY(); getFinalX(); and getCurrY() getCurrX()
getDuration()

这意味着您必须创建一个 Scroller。

如果你想使用触摸,你也可以使用 GestureDetector 并定义你自己的画布滚动。以下示例正在创建一个 ScrollableImageView,为了使用它,您必须定义图像的测量值。您可以定义自己的滚动范围,并在完成滚动后重新绘制图像。

http://www.anddev.org/viewtopic.php?p=31487#31487

根据您的代码,您应该考虑 invalidate(int l, int t, int r, int b); 为无效。

于 2010-02-06T13:21:46.257 回答
3
SimpleOnGestureListener.onFling() 

它似乎发生在滚动结束时(即用户放开手指),这就是我正在使用的,它对我很有用。

于 2010-09-30T18:25:54.297 回答
2

几个月后回到这个问题,我现在采用了不同的策略:使用处理程序(如在 Android Snake 示例中)每 125 毫秒向应用程序发送一条消息,提示它检查是否已启动 Scroll 和自上次滚动事件以来是否已超过 100 毫秒。

这似乎工作得很好,但如果有人能看到任何缺点或可能的改进,我将不胜感激。

相关代码在 MyView 类中:

public class MyView extends android.view.View {

...

private long timeCheckInterval = 125; // milliseconds
private long scrollEndInterval = 100;
public long latestScrollEventTime;
public boolean scrollInProgress = false;

public MyView(Context context) {
    super(context);
}

private timeCheckHandler mTimeCheckHandler = new timeCheckHandler();

class timeCheckHandler extends Handler{

        @Override
        public void handleMessage(Message msg) {
        long now = System.currentTimeMillis();
        if (scrollInProgress && (now>latestScrollEventTime+scrollEndInterval)) {
                    scrollInProgress = false;

// 滚动已经结束,所以在这里插入代码

// 调用 doDrawing() 方法

// 重绘位图,在滚动结束的地方重新居中

                    [ layout or view ].invalidate();
        }
        this.sleep(timeCheckInterval);
        }

        public void sleep(long delayMillis) {
            this.removeMessages(0);
            sendMessageDelayed(obtainMessage(0), delayMillis);
            }
    }
}

@Override protected void onDraw(Canvas canvas){
        super.onDraw(canvas);

// 将大缓冲区位图绘制到视图画布上的代码 // 定位以考虑正在进行的任何滚动

}

public void doDrawing() {

// 用于在大缓冲区位图上进行详细(且耗时)绘图的代码

// 以下指令重置 Time Check 时钟 // 时钟首次启动时 // 主活动在应用程序启动时调用此方法

        mTimeCheckHandler.sleep(timeCheckInterval);
}

// MyView 类的其余部分

}

在 MyGestureDetector 类中

public class MyGestureDetector extends SimpleOnGestureListener {

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
        float distanceY) {

    [MyView].scrollInProgress = true;
        long now = System.currentTimeMillis();  
    [MyView].latestScrollEventTime =now;

    [MyView].scrollX += (int) distanceX;
    [MyView].scrollY += (int) distanceY;

// 下一条指令导致调用 View 的 onDraw 方法 // 它将缓冲区位图绘制到屏幕上 // 移动以考虑滚动

    [MyView].invalidate();

}

// MyGestureDetector 类的其余部分

}

于 2010-08-19T20:58:42.737 回答
1

我正在研究同样的问题。我看到 Akos Cz 回答了你的问题。我创建了类似的东西,但在我的版本中,我注意到它只适用于常规滚动 - 意思是不会产生一掷千金的滚动。但是,如果确实生成了一个投掷 - 无论我是否处理了一个投掷,那么它都没有检测到“onTouchEvent”中的“ACTION_UP”。现在也许这只是我的实现,但如果是我不知道为什么。

经过进一步调查,我注意到在投掷期间,“ACTION_UP”每次都被传递到“e2”中的“onFling”。所以我认为这一定是为什么在这些情况下没有在“onTouchEvent”中处理它的原因。

为了让它对我有用,我只需要调用一个方法来处理“onFling”中的“ACTION_UP”,然后它就适用于两种类型的滚动。以下是我在应用中实施的具体步骤:

- 在构造函数中将“gestureScrolling”布尔值初始化为“false”。

-我在“onScroll”中将其设置为“true”

-创建了一种方法来处理“ACTION_UP”事件。在该事件中,我将“gestureSCrolling”重置为 false,然后完成了我需要做的其余处理。

-在“onTouchEvent”中,如果检测到“ACTION_UP”并且“gestureScrolling”=true,那么我调用我的方法来处理“ACTION_UP”

- 我所做的不同之处在于:我还调用了我的方法来处理“onFling”中的“ACTION_UP”。

于 2010-12-02T02:43:16.627 回答
1

我相信这对你来说为时已晚,但是,我似乎已经找到了解决你最初问题的正确方法,而不是必要的意图。

如果您使用 Scroller/OverScroller 对象进行滚动,您应该检查以下函数的返回值。

public boolean computeScrollOffset() 

请享用

收割机

于 2012-03-13T11:18:02.487 回答
1

我认为这将根据您的需要工作

protected class SnappingGestureDetectorListener extends SimpleOnGestureListener{

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY){
        boolean result = super.onScroll(e1, e2, distanceX, distanceY);

        if(!result){
            //Do what you need to do when the scrolling stop here
        }

        return result;
    }

}
于 2013-03-22T14:35:54.310 回答
1

这对我有用。

我用onFingerUp()方法丰富了现有的GestureDetector.OnGestureListener 。该侦听器作为内置的 GestureDetector 执行所有操作,它还可以侦听手指向上事件(它不是 onFling(),因为仅当手指抬起并伴随快速滑动动作时才会调用)。

import android.content.Context;
import android.os.Handler;
import android.view.GestureDetector;
import android.view.MotionEvent;

public class FingerUpGestureDetector extends GestureDetector {
    FingerUpGestureDetector.OnGestureListener fListener;
    public FingerUpGestureDetector(Context context, OnGestureListener listener) {
        super(context, listener);
        fListener = listener;
    }

    public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, OnGestureListener fListener) {
        super(context, listener);
        this.fListener = fListener;
    }

    public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler, OnGestureListener fListener) {
        super(context, listener, handler);
        this.fListener = fListener;
    }

    public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler, boolean unused, OnGestureListener fListener) {
        super(context, listener, handler, unused);
        this.fListener = fListener;
    }

    public interface OnGestureListener extends GestureDetector.OnGestureListener {
        boolean onFingerUp(MotionEvent e);
    }

    public static class SimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements FingerUpGestureDetector.OnGestureListener {
        @Override
        public boolean onFingerUp(MotionEvent e) {
            return false;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (super.onTouchEvent(ev)) return true;
        if (ev.getAction() == MotionEvent.ACTION_UP) {
            return fListener.onFingerUp(ev);
        }
        return false;
    }
}
于 2015-04-27T14:47:21.123 回答
0

我自己没有这样做,但是查看 onTouch() 你总是得到一个序列 0<2>1,所以手指抬起的结尾必须是 1。

于 2010-01-27T13:36:13.670 回答
0

我不了解 Android,但查看文档似乎 Rob 是对的:Android ACTION_UP constant Try checks for ACTION_UP from getAction()?

编辑: e1.getAction() 显示什么?它会返回 ACTION_UP 吗?文档说它保存了初始的向下事件,所以当指针向上时它可能也会通知

编辑:我只能想到两件事。你在任何时候都返回假吗?这可能会阻止 ACTION_UP

我要尝试的唯一另一件事是有一个单独的事件,可能是 onDown,并在 onScroll 中设置一个标志,例如 isScrolling。当将 ACTION_UP 赋予 onDown 并设置 isScrolling 时,您可以做任何您想做的事情并将 isScrolling 重置为 false。也就是说,假设onDown和onScroll一起被调用,getAction会在onDown期间返回ACTION_UP

于 2010-02-04T01:59:45.967 回答
0

我没有尝试/使用过这个,但是一个方法的想法:

在每个滚动事件上停止/中断重绘画布等待 1 秒,然后在每个滚动事件上开始重绘画布。

这将导致仅在滚动结束时执行重绘,因为只有最后一个滚动实际上不会中断以完成重绘。

希望这个想法对你有帮助:)

于 2010-02-06T18:06:02.550 回答
0

从 GestureListener API 的 onScroll 事件中提取:链接文本

public abstract boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) 自:API 级别 1

如果事件被消费,则返回 * true,否则返回 false

也许一旦事件被消费,动作就完成了,用户已经将手指从屏幕上移开,或者至少完成了这个 onScroll 动作

然后,您可以在 IF 语句中使用它来扫描 == true,然后开始下一个操作。

于 2010-02-10T11:52:32.987 回答
0

我尝试向手势检测器添加附加功能。希望它可以帮助某人更好地利用他的时间...

https://gist.github.com/WildOrangutan/043807ddbd68978179b7cea3b53c71e8

于 2018-08-09T13:51:18.327 回答
0

如果您SimpleGestureDetector用于处理滚动事件,则可以执行此操作

fun handleTouchEvents(event: MotionEvent): Boolean {
    if(event.action == ACTION_UP) yourListener.onScrollEnd()
    return gestureDetector.onTouchEvent(event)
}
于 2020-06-25T14:18:05.580 回答