Android:如何检测滚动何时结束

时间:2010-01-18 22:01:46

标签: android android-canvas smooth-scrolling

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

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

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

13 个答案:

答案 0 :(得分:67)

以下是我解决问题的方法。希望这会有所帮助。

// 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);
}

答案 1 :(得分: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);失效。

答案 2 :(得分:3)

SimpleOnGestureListener.onFling() 

当滚动结束时(即用户让手指离开)似乎会发生,这就是我正在使用的东西,它对我来说很有用。

答案 3 :(得分:1)

几个月后回到这里,我现在采取了不同的策略:使用Handler(如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() {

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

//以下指令重置时间检查时钟 //时钟首先启动时 //主要活动在应用启动时调用此方法

        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类的其余部分

}

答案 4 :(得分:1)

我正在调查同样的问题。我看到Akos Cz回答了你的问题。我创建了类似的东西,但是在我的版本中我注意到它只适用于常规滚动 - 这意味着它不会产生任何影响。但是如果确实产生了投掷 - 无论我是否处理过一次投掷,那么它都没有检测到“onTouchEvent”中的“ACTION_UP”。现在也许这只是我的实现,但如果它是我无法弄清楚为什么。

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

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

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

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

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

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

- 我所做的不同的部分是:我还调用了我的方法来处理“onFling”中的“ACTION_UP”。

答案 5 :(得分:1)

这对我有用。

我使用 onFingerUp()方法丰富了现有的 GestureDetector.OnGestureListener 。这个监听器作为内置的GestureDetector执行所有操作,它也可以监听手指向上事件(它不是onFling(),因为只有当手指被抬起并且快速滑动动作时才会调用它。) / p>

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;
    }
}

答案 6 :(得分:0)

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

答案 7 :(得分:0)

我不知道Android,但看看文档似乎Rob是对的:Android ACTION_UP constant尝试从getAction()检查ACTION_UP?

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

编辑:我能想到的另外两件事。你在任何时候都是假的?这可能会阻止ACTION_UP

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

答案 8 :(得分:0)

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

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

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

希望这个想法可以帮助你:)

答案 9 :(得分:0)

从GestureListener API中提取onScroll事件:link text

  

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

     

<强>返回       *如果事件被消耗,则为true,否则为false

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

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

答案 10 :(得分:0)

我认为这会在你需要的时候起作用

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;
    }

}

答案 11 :(得分:0)

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

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

答案 12 :(得分:0)

如果您使用SimpleGestureDetector处理滚动事件,则可以这样做

fun handleTouchEvents(event: MotionEvent): Boolean {
    if(event.action == ACTION_UP) yourListener.onScrollEnd()
    return gestureDetector.onTouchEvent(event)
}