Android:在ScrollView停止滚动时检测

use*_*691 82 android scrollview

ScrollView在Android中使用了一个,其中可见部分的ScrollView大小与其中一个单元格的大小相同Scrollview.每个"细胞"都是相同的高度.因此,我想要做的是ScrollView在滚动后捕捉到位置.

目前我正在检测用户何时触摸,ScrollView以及何时开始滚动并从那里开始工作,但它非常错误.当用户只是轻弹它并滚动然后减速时,它也需要工作.

在iPhone上有一个类似的功能,didDecelerateScrollView完成滚动后我可以做任何我想要的代码.Android有这样的东西吗?或者是否有一些我可以看到的代码来找出更好的方法呢?

我查看了Android文档,找不到类似的东西.

tul*_*84z 93

我最近不得不实现你描述的功能.我做的是让一个Runnable检查出ScrollView是否已经停止滚动,当首次使用在变量newCheck定义的时间之后返回的值触发onTouchEvent时,比较getScrollY()返回的值.

见下面的代码(工作解决方案):

public class MyScrollView extends ScrollView{

private Runnable scrollerTask;
private int initialPosition;

private int newCheck = 100;
private static final String TAG = "MyScrollView";

public interface OnScrollStoppedListener{
    void onScrollStopped();
}

private OnScrollStoppedListener onScrollStoppedListener;

public MyScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);

    scrollerTask = new Runnable() {

        public void run() {

            int newPosition = getScrollY();
            if(initialPosition - newPosition == 0){//has stopped

                if(onScrollStoppedListener!=null){

                    onScrollStoppedListener.onScrollStopped();
                }
            }else{
                initialPosition = getScrollY();
                MyScrollView.this.postDelayed(scrollerTask, newCheck);
            }
        }
    };
}

public void setOnScrollStoppedListener(MyScrollView.OnScrollStoppedListener listener){
    onScrollStoppedListener = listener;
}

public void startScrollerTask(){

    initialPosition = getScrollY();
    MyScrollView.this.postDelayed(scrollerTask, newCheck);
}

}
Run Code Online (Sandbox Code Playgroud)

然后我有:

scroll.setOnTouchListener(new OnTouchListener() {

        public boolean onTouch(View v, MotionEvent event) {

            if (event.getAction() == MotionEvent.ACTION_UP) {

                scroll.startScrollerTask();
            }

            return false;
        }
});
scroll.setOnScrollStoppedListener(new OnScrollStoppedListener() {

        public void onScrollStopped() {

            Log.i(TAG, "stopped");

        }
});
Run Code Online (Sandbox Code Playgroud)

顺便说一下,我用其他回复中的一些想法在我的应用程序中执行此操作.希望这可以帮助.有任何问题请随时询问我(们).干杯.

  • 谢谢,一直在寻找这样的年龄.用于代替Horizo​​ntalScrollView,它(显然)也可以使用.只有getScrollX()必须被getScrollX()取代 (11认同)
  • 都在这里试过了。这是唯一有效的方法。 (2认同)

con*_*ept 22

这是另一个修复,恕我直言,在ScrollView中缺少OnEndScroll事件错误.

它的灵感来自于朴实的答案.只需将此类放入您的项目(更改包以匹配您自己的)并使用下面的xml

package com.thecrag.components.ui;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;

public class ResponsiveScrollView extends ScrollView {

    public interface OnEndScrollListener {
        public void onEndScroll();
    }

    private boolean mIsFling;
    private OnEndScrollListener mOnEndScrollListener;

    public ResponsiveScrollView(Context context) {
        this(context, null, 0);
    }

    public ResponsiveScrollView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ResponsiveScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void fling(int velocityY) {
        super.fling(velocityY);
        mIsFling = true;
    }

    @Override
    protected void onScrollChanged(int x, int y, int oldX, int oldY) {
        super.onScrollChanged(x, y, oldX, oldY);
        if (mIsFling) {
            if (Math.abs(y - oldY) < 2 || y >= getMeasuredHeight() || y == 0) {
                if (mOnEndScrollListener != null) {
                    mOnEndScrollListener.onEndScroll();
                }
                mIsFling = false;
            }
        }
    }

    public OnEndScrollListener getOnEndScrollListener() {
        return mOnEndScrollListener;
    }

    public void setOnEndScrollListener(OnEndScrollListener mOnEndScrollListener) {
        this.mOnEndScrollListener = mOnEndScrollListener;
    }

}
Run Code Online (Sandbox Code Playgroud)

再次更改包名称以匹配您的项目

<com.thecrag.components.ui.ResponsiveScrollView
        android:id="@+id/welcome_scroller"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/welcome_scroll_command_help_container"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/welcome_header_text_thecrag"
        android:layout_margin="6dp">
    ....
</com.thecrag.components.ui.ResponsiveScrollView>
Run Code Online (Sandbox Code Playgroud)

  • +1很好,简单的答案.我将差值"2"更改为"0.5f"以反映原始的"ScrollView"的"MAX_SCROLL_FACTOR"值. (2认同)
  • 这个实现很不错但是在开始时没有填充滚动视图时它失败了.这是一个解决方法(对于水平滚动视图,所以将所有x替换为y以获得垂直滚动视图):@ Override protected void onScrollChanged(int x,int y,int oldX,int oldY){super.onScrollChanged(x,y) ,oldX,oldY); if(Math.abs(x - oldX)<SCROLL_FINISHED_THRESHOLD && x!= lastStopX || x> = getMeasuredWidth()|| x == 0){lastStopX = x; if(mOnEndScrollListener!= null){mOnEndScrollListener.onScrollEnded(); }}} (2认同)

Ros*_*ick 9

我将(水平)ScrollView子类化并做了类似这样的事情:

@Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
    if (Math.abs(x - oldX) > SlowDownThreshold) {  
        currentlyScrolling = true;
    } else {
        currentlyScrolling = false;
        if (!currentlyTouching) {
            //scrolling stopped...handle here
        }
    }
    super.onScrollChanged(x, y, oldX, oldY);
}
Run Code Online (Sandbox Code Playgroud)

我为SlowDownThreshold使用值1,因为它似乎总是最后一次onScrollChanged事件的差异.

为了在慢慢拖动时使其行为正常,我必须这样做:

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            currentlyTouching = true;
    }
    return super.onInterceptTouchEvent(event);
}

@Override
public boolean onTouch(View view, MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            currentlyTouching = false;
            if (!currentlyScrolling) {
                //I handle the release from a drag here
                return true;
            }
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)


Lin*_*eng 7

我的方法是每次调用onScrollChanged()时通过更改时间戳来确定滚动状态.可以很容易地确定滚动的开始和结束时间.您还可以更改阈值(我使用100毫秒)来修复灵敏度.

public class CustomScrollView extends ScrollView {
    private long lastScrollUpdate = -1;

    private class ScrollStateHandler implements Runnable {

        @Override
        public void run() {
            long currentTime = System.currentTimeMillis();
            if ((currentTime - lastScrollUpdate) > 100) {
                lastScrollUpdate = -1;
                onScrollEnd();
            } else {
                postDelayed(this, 100);
            }
        }
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (lastScrollUpdate == -1) {
            onScrollStart();
            postDelayed(new ScrollStateHandler(), 100);
        }

        lastScrollUpdate = System.currentTimeMillis();
    }

    private void onScrollStart() {
        // do something
    }

    private void onScrollEnd() {
        // do something
    }

}
Run Code Online (Sandbox Code Playgroud)


小智 5

我对这个问题的方法是使用计时器来检查以下 2 个“事件”。

1) onScrollChanged() 停止被调用

2)用户的手指从滚动视图中抬起

public class CustomScrollView extends HorizontalScrollView {

public CustomScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

Timer ntimer = new Timer();
MotionEvent event;

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) 
{
    checkAgain();
    super.onScrollChanged(l, t, oldl, oldt);
}

public void checkAgain(){
    try{
        ntimer.cancel();
        ntimer.purge();
    }
    catch(Exception e){}
    ntimer = new Timer();
    ntimer.schedule(new TimerTask() {

        @Override
        public void run() {

            if(event.getAction() == MotionEvent.ACTION_UP){
                // ScrollView Stopped Scrolling and Finger is not on the ScrollView
            }
            else{
                // ScrollView Stopped Scrolling But Finger is still on the ScrollView
                checkAgain();
            }
        }
    },100);

}

@Override
public boolean onTouchEvent(MotionEvent event) {
    this.event = event; 
    return super.onTouchEvent(event);
    }
}
Run Code Online (Sandbox Code Playgroud)


Ale*_*arf 5

在我看来,这是又一个简单而干净的解决方案,它自然受到上述答案的启发。基本上,在短暂延迟(此处为50ms)之后,用户结束手势后,检查getScrollY()是否仍在更改。

public class ScrollViewWithOnStopListener extends ScrollView {

    OnScrollStopListener listener;

    public interface OnScrollStopListener {
        void onScrollStopped(int y);
    }

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

    public ScrollViewWithOnStopListener(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_UP:
                checkIfScrollStopped();
        }

        return super.onTouchEvent(ev);
    }

    int initialY = 0;

    private void checkIfScrollStopped() {
        initialY = getScrollY();
        this.postDelayed(new Runnable() {
            @Override
            public void run() {
                int updatedY = getScrollY();
                if (updatedY == initialY) {
                    //we've stopped
                    if (listener != null) {
                        listener.onScrollStopped(getScrollY());
                    }
                } else {
                    initialY = updatedY;
                    checkIfScrollStopped();
                }
            }
        }, 50);
    }

    public void setOnScrollStoppedListener(OnScrollStopListener yListener) {
        listener = yListener;
    }
}
Run Code Online (Sandbox Code Playgroud)


kas*_*rch 2

尝试在 StackOverflow 上查看这个问题- 它与您的问题并不完全相同,但它提供了如何管理ScrollView.

基本上,您需要CustomScrollView通过扩展ScrollView和覆盖来创建自己的onScrollChanged(int x, int y, int oldx, int oldy)。然后您需要在布局文件中引用它,而不是ScrollViewcom.mypackage.CustomScrollView.