如何获取NestedScrollView滚动状态及强制停止滚动?
刚好我之前也遇到过类似的问题,给你分享两个靠谱的解决方案:
一、强制停止NestedScrollView滚动并调用scrollTo()
你遇到的scrollTo()不生效的问题,本质是因为NestedScrollView正在执行滚动动画,直接调用scrollTo()会被动画覆盖。解决思路是先终止它的滚动动画,再执行滚动操作。
NestedScrollView内部用Scroller来处理滚动动画,但这个成员是私有的,我们可以通过反射拿到它并停止动画:
// 工具方法:停止NestedScrollView的滚动动画 private void stopNestedScrollAnimation(NestedScrollView scrollView) { try { // 获取私有成员mScroller Field scrollerField = NestedScrollView.class.getDeclaredField("mScroller"); scrollerField.setAccessible(true); Scroller scroller = (Scroller) scrollerField.get(scrollView); // 如果动画正在执行,就终止它 if (scroller != null && !scroller.isFinished()) { scroller.abortAnimation(); } } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } }
然后修改你的滚动代码,先停止动画再调用scrollTo():
scrollView.post(new Runnable() { @Override public void run() { stopNestedScrollAnimation(scrollView); scrollView.scrollTo(0, contentHeadingView.getTop()); } });
这样就能确保scrollTo()立刻生效,不会被原有滚动动画干扰。
二、监听NestedScrollView的滚动开始与停止
原生的OnScrollChangeListener只能拿到滚动位置,没法直接判断滚动的开始和停止状态。我们可以自定义一个ObservableNestedScrollView,通过监听滚动变化结合延迟回调来实现状态判断:
public class ObservableNestedScrollView extends NestedScrollView { private OnScrollStateChangeListener mScrollStateListener; private final Runnable mScrollStopRunnable = () -> { if (mScrollStateListener != null) { mScrollStateListener.onScrollStopped(); } }; public ObservableNestedScrollView(Context context) { super(context); } public ObservableNestedScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public ObservableNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (mScrollStateListener == null) return; // 位置发生变化,说明正在滚动 if (t != oldt) { // 移除之前的停止回调(因为还在滚动) removeCallbacks(mScrollStopRunnable); // 第一次滚动时触发开始回调 if (oldt == 0 || Math.abs(t - oldt) > 0) { mScrollStateListener.onScrollStarted(); } // 触发滚动中回调(可选) mScrollStateListener.onScrolling(l, t); // 延迟100ms判断是否停止滚动,可根据需求调整延迟时间 postDelayed(mScrollStopRunnable, 100); } } // 滚动状态回调接口 public interface OnScrollStateChangeListener { void onScrollStarted(); // 滚动开始 void onScrolling(int scrollX, int scrollY); // 滚动中(可选) void onScrollStopped(); // 滚动停止 } public void setOnScrollStateChangeListener(OnScrollStateChangeListener listener) { mScrollStateListener = listener; } }
使用的时候,直接在布局里替换成这个自定义View,然后设置监听:
ObservableNestedScrollView scrollView = findViewById(R.id.your_scroll_view); scrollView.setOnScrollStateChangeListener(new ObservableNestedScrollView.OnScrollStateChangeListener() { @Override public void onScrollStarted() { // 这里处理滚动开始的逻辑 Log.d("ScrollState", "滚动开始了"); } @Override public void onScrolling(int scrollX, int scrollY) { // 可选:处理滚动中的实时逻辑 } @Override public void onScrollStopped() { // 这里处理滚动停止的逻辑 Log.d("ScrollState", "滚动停止了"); } });
这个方案的核心是:每次滚动时重置停止回调的延迟,如果100ms内没有新的滚动事件,就认为滚动已经停止,触发停止回调。延迟时间可以根据你的需求调整,一般100ms足够识别滚动结束。
内容的提问来源于stack exchange,提问作者Mathan Chinna
相关产品推荐
相关产品推荐

