我可以为ScrollView使用onScrollListener吗?

arn*_*rnp 134 android listener scrollview

HorizontalScrollView在布局中使用a ,我需要确定用户已达到滚动的起点和终点.

因为ListView我已经尝试过了onScrollListener,可以找到滚动的起点和终点.

我尝试在我做同样的事情,Scrollview但似乎不可能.有没有其他可能的方法来实现我的需要.

提前致谢.

Zbu*_*bun 375

每个View调用实例getViewTreeObserver().现在,当持有实例时ViewTreeObserver,您可以OnScrollChangedListener()使用该方法添加一个实例addOnScrollChangedListener().

您可以在此处查看有关此课程的更多信息.

它可以让您了解每个滚动事件 - 但没有坐标.您可以通过使用getScrollY()getScrollX()从侦听器中获取它们.

scrollView.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() {
    @Override
    public void onScrollChanged() {
        int scrollY = rootScrollView.getScrollY(); // For ScrollView
        int scrollX = rootScrollView.getScrollX(); // For HorizontalScrollView
        // DO SOMETHING WITH THE SCROLL COORDINATES
    }
});
Run Code Online (Sandbox Code Playgroud)

  • 这个答案应该标记正确.`hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener(){@Override public void onScrollChanged(){Log.i(TAG,"scroll:"+ hsv.getScrollX());}});`on onStart( )`hsv`是`Horizo​​ntalScrollView`的工作原理.我怀疑同样适用于ScrollView. (6认同)
  • 答案中的示例代码引入了内存泄漏.由于该方法是`add`而不是`set`,因此所有侦听器都将被保留,直到被明确删除.因此,在样本中用作侦听器实现的匿名类将泄漏(以及它引用的任何内容,即外部类). (6认同)
  • 可能是一个愚蠢的问题,但是什么是rootScrollView? (6认同)
  • 您应首先检查[ViewTreeObserver.isAlive()](http://developer.android.com/reference/android/view/ViewTreeObserver.html#isAlive()) (5认同)
  • 究竟.这是最好的答案.无需扩展Horizo​​ntalScrollView.刚用ScrollView测试,效果很好:最终ScrollView sv =(ScrollView)findViewById(R.id.scrollViewArticle); sv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener(){@ Overver public void onScrollChanged(){Log.d("onScrollChanged Y",String.valueOf(sv.getScrollY()))}}}); (3认同)

Mid*_*mar 42

这可能非常有用.用NestedScrollView而不是ScrollView.支持库23.1引入了一个OnScrollChangeListenerNestedScrollView.所以你可以做这样的事情.

 myScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
        @Override
        public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
            Log.d("ScrollView","scrollX_"+scrollX+"_scrollY_"+scrollY+"_oldScrollX_"+oldScrollX+"_oldScrollY_"+oldScrollY);
            //Do something
        }
    });
Run Code Online (Sandbox Code Playgroud)

  • 它需要API级别23,即Android M. (6认同)
  • @ dazed'n'confused`NestedScrollView`是支持库的一部分,它的`setOnScrollChangeListener`方法没有最低版本要求. (5认同)
  • 不要与需要 API 级别 23 的 `View` 的 `setOnScrollChangeListener` 混淆 (5认同)
  • 绝对比使用 getViewTreeObserver() 跟踪您之前是否添加过侦听器更容易。谢谢! (2认同)

ZaB*_*anc 17

这是我编写的派生的Horizo​​ntalScrollView,用于处理有关滚动和滚动结尾的通知.它可以在用户停止主动滚动时以及在用户放开后完全减速时正确处理:

public class ObservableHorizontalScrollView extends HorizontalScrollView {
    public interface OnScrollListener {
        public void onScrollChanged(ObservableHorizontalScrollView scrollView, int x, int y, int oldX, int oldY);
        public void onEndScroll(ObservableHorizontalScrollView scrollView);
    }

    private boolean mIsScrolling;
    private boolean mIsTouching;
    private Runnable mScrollingRunnable;
    private OnScrollListener mOnScrollListener;

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

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

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

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int action = ev.getAction();

        if (action == MotionEvent.ACTION_MOVE) {
            mIsTouching = true;
            mIsScrolling = true;
        } else if (action == MotionEvent.ACTION_UP) {
            if (mIsTouching && !mIsScrolling) {
                if (mOnScrollListener != null) {
                    mOnScrollListener.onEndScroll(this);
                }
            }

            mIsTouching = false;
        }

        return super.onTouchEvent(ev);
    }

    @Override
    protected void onScrollChanged(int x, int y, int oldX, int oldY) {
        super.onScrollChanged(x, y, oldX, oldY);

        if (Math.abs(oldX - x) > 0) {
            if (mScrollingRunnable != null) {
                removeCallbacks(mScrollingRunnable);
            }

            mScrollingRunnable = new Runnable() {
                public void run() {
                    if (mIsScrolling && !mIsTouching) {
                        if (mOnScrollListener != null) {
                            mOnScrollListener.onEndScroll(ObservableHorizontalScrollView.this);
                        }
                    }

                    mIsScrolling = false;
                    mScrollingRunnable = null;
                }
            };

            postDelayed(mScrollingRunnable, 200);
        }

        if (mOnScrollListener != null) {
            mOnScrollListener.onScrollChanged(this, x, y, oldX, oldY);
        }
    }

    public OnScrollListener getOnScrollListener() {
        return mOnScrollListener;
    }

    public void setOnScrollListener(OnScrollListener mOnEndScrollListener) {
        this.mOnScrollListener = mOnEndScrollListener;
    }

}
Run Code Online (Sandbox Code Playgroud)

  • @ZaBlanc - 你能告诉我如何从我的Activity中初始化这个类和监听器,它包含ScrollView吗?我已经很好地实现了它,但是听众不会返回任何值. (3认同)

Cri*_*tan 11

您可以使用NestedScrollView代替ScrollView. 但是,当使用 Kotlin Lambda 时,它不会知道您想要 NestedScrollViewsetOnScrollChangeListener而不是 View(这是 API 级别 23)。您可以通过将第一个参数指定为 NestedScrollView 来解决此问题。

nestedScrollView.setOnScrollChangeListener { _: NestedScrollView, scrollX: Int, scrollY: Int, _: Int, _: Int ->
    Log.d("ScrollView", "Scrolled to $scrollX, $scrollY")
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是可接受的答案,因为使用 viewTreeObserver 的方法会导致内存泄漏,必须手动删除,否则将添加多个滚动观察者。 (3认同)

小智 6

如果你想知道一个视图的滚动位置,那么你可以在 View 类上使用以下扩展函数:

fun View?.onScroll(callback: (x: Int, y: Int) -> Unit) {
    var oldX = 0
    var oldY = 0
    this?.viewTreeObserver?.addOnScrollChangedListener {
        if (oldX != scrollX || oldY != scrollY) {
            callback(scrollX, scrollY)
            oldX = scrollX
            oldY = scrollY
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 6

除了接受的答案外,您还需要持有听众的参考,并在不需要时将其删除。否则,您将收到 ScrollView 和内存泄漏的空指针异常(在已接受答案的评论中提到)

  1. 您可以在您的活动/片段中实现 OnScrollChangedListener。

    MyFragment : ViewTreeObserver.OnScrollChangedListener
    
    Run Code Online (Sandbox Code Playgroud)
  2. 当您的视图准备就绪时,将其添加到 scrollView。

    scrollView.viewTreeObserver.addOnScrollChangedListener(this)
    
    Run Code Online (Sandbox Code Playgroud)
  3. 不再需要时删除侦听器(即 onPause())

    scrollView.viewTreeObserver.removeOnScrollChangedListener(this)
    
    Run Code Online (Sandbox Code Playgroud)