Android 滚动视图 onScrollChanged

Posted

技术标签:

【中文标题】Android 滚动视图 onScrollChanged【英文标题】:Android scrollview onScrollChanged 【发布时间】:2011-05-14 20:46:18 【问题描述】:

我在滚动视图内的文本视图中有固定内容。当用户滚动到某个位置时,我想启动一个活动或触发一个Toast

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_ android:layout_
 android:id="@+id/scroller">
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical" android:layout_
  android:layout_>
  <TextView android:layout_ android:id="@+id/story"
   android:layout_ android:text="@string/lorem"
   android:gravity="fill" />
 </LinearLayout>
</ScrollView>

我的问题是在实现受保护的方法onScrollChanged 以找出滚动视图的位置。

我找到了this 的答案,有没有更简单的解决方案,而不是声明一个界面,覆盖我发布的链接上的滚动视图等?

【问题讨论】:

你可以使用这个***.com/questions/10316743/detect-end-of-scrollview 【参考方案1】:

有一种比继承ScrollView 更简单的方法。 ScrollViewViewTreeObserver 对象可用于监听滚动。

由于ViewTreeObserver 对象在ScrollView 的生命周期内可能会发生变化,因此我们需要在ScrollView 上注册一个OnTouchListener,以便在滚动时获得ViewTreeObserver

final ViewTreeObserver.OnScrollChangedListener onScrollChangedListener = new
                           ViewTreeObserver.OnScrollChangedListener() 

    @Override
    public void onScrollChanged() 
        //do stuff here 
    
;

final ScrollView scrollView = (ScrollView) findViewById(R.id.scroller);
scrollView.setOnTouchListener(new View.OnTouchListener() 
    private ViewTreeObserver observer;

    @Override
    public boolean onTouch(View v, MotionEvent event) 
        if (observer == null) 
            observer = scrollView.getViewTreeObserver();
            observer.addOnScrollChangedListener(onScrollChangedListener);
        
        else if (!observer.isAlive()) 
            observer.removeOnScrollChangedListener(onScrollChangedListener);
            observer = scrollView.getViewTreeObserver();
            observer.addOnScrollChangedListener(onScrollChangedListener);
        

        return false;
    
);

【讨论】:

这并不一定比继承ScrollView 容易。它实际上是更多的代码。如果你是子类,你可以重写 onScrollChanged 方法。 我试过这个,对于相同的滚动 Y 值,它似乎触发了 20 次或更多次事件。看起来不太理想。 嗯……这段代码有一个大问题!这个问题导致了@RandomEngy评论中的错误。因此,我不知道为什么它的投票率如此之高。它在 SCROLL 上的每个触摸事件上添加此侦听器。因此,基本上,如果您单击滚动 20 次,它将导致对侦听器的 20 次调用。侦听器应该只添加一次。例如像这样:scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() @Override public void onScrollChanged() //do stuff );。不在触摸方法内 或者更糟的是,它可能会在示例中添加至少 40 个侦听器(每次点击至少 TOUCH_DOWN 和 TOUCH_UP)。 我有OnTouchListener,因为documentation 说“返回的 ViewTreeObserver 观察者不能保证在此视图的生命周期内保持有效”。我已经编辑了答案,因此如果 ViewTreeObserver 不再存在,对其的回调将被删除并添加到 View 的当前 ViewtreeObserver【参考方案2】:

没有。

ScrollView 不提供滚动事件的侦听器,甚至不提供检查用户向下滚动多远的方法,因此您必须按照链接的建议进行操作。

【讨论】:

有一个方法 onScrollChanged 可以被覆盖。同样可以使用getScrollX,getScrollY。 现在看来这个答案是无效的。从 API 23 (M) 开始,有一个名为“OnScrollChangeListener”的监听器 scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() @Override public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) //使用参数 ); @S.D.NChanakaFernando 还应该注意的是,在滚动方面,它仍然没有提供针对具体细节的内置解决方案。例如,如果您想检测滚动的结束,则必须使用 onScrollChangeListener 手动执行此操作,这非常烦人。以下是遇到此问题的任何人的一些建议:***.com/questions/8181828/…【参考方案3】:

这个问题已经相当老了,但万一有人路过(比如我):

API 23 开始,Android 的 View 有一个 OnScrollChangeListener 和 matching setter。

支持库中的NestedScrollView 还支持在该 API 级别之前设置滚动侦听器。据我所知,NestedScrollView 可以用来替代普通的ScrollView,没有任何问题。

【讨论】:

没有向后兼容性 @define 很好,支持库中有NestedScrollView,你可以使用它而不是常规的Scrollview 没有问题 对不起,由于某种原因,我错过了你提到它的部分。无论如何,谢谢 @define 我只是没有提到您可以使用 NestedScrollView 而不是普通的。我会把它添加到答案中。 这很棒。我们支持 API 18+,我真的很想要这个库存功能。谢谢!【参考方案4】:

实际上有一种方法可以知道用户滚动了多远。 ScrollView 的 getScrollY() 方法告诉你。

【讨论】:

不,据此:***.com/questions/2132370/… 我在公司的一个项目中使用了这个解决方案。在我在这里发布一些东西之前,我会测试一切。也许你也应该这样做? 抱歉,误读了问题。它适用于ScrollView,但不适用于ListViewGridView【参考方案5】:

我扩展了我的滚动视图。 This link 可能会有所帮助。

class MyScroll extends ScrollView 
    boolean onTop=true;
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) 
        //Log.d(TAG, "scroll changed: " + this.getTop() + " "+t);
        if(t <= 0)
            onTop = true;
            //Log.d(TAG, "scroll top: " + t);
            super.onScrollChanged(l, t, oldl, oldt);
            return;
            // reaches the top end
        
        onTop = false;

        View view = (View) getChildAt(getChildCount()-1);
        int diff = (view.getBottom()-(getHeight()+getScrollY()+view.getTop()));// Calculate the scrolldiff
        if( diff <= 0 )
            // if diff is zero, then the bottom has been reached
        
        super.onScrollChanged(l, t, oldl, oldt);
    

【讨论】:

【参考方案6】:

您可以使用此触发器来显示 Toast 或 Start Activity

 scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() 
        @Override
        public void onScrollChanged() 
             // do something when Scroll  
        
    );

【讨论】:

【参考方案7】:

您可以使用此代码来检测是向上还是向下滚动

scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() 
            int lastScroll=0;
            @Override
            public void onScrollChanged() 
                int scrollY = scrollView.getScrollY(); // For ScrollView herzontial use getScrollX()

                if (scrollY > lastScroll ) 
                    Log.e("scroll","down scroll"+scrollY);
                 else if (scrollY < lastScroll ) 
                    Log.e("scroll","up scroll"+scrollY);
                
                lastScroll=scrollY;
            
        );

【讨论】:

【参考方案8】:

NestedScrollView 与 android.widget.ScrollView 类似,但它支持在新旧版本的 Android 上同时充当嵌套滚动父项和子项。

1st - 使用 NestedScrollView 绑定而不是 ScrollView

2nd - 像这样设置滚动监听器,以通过示例检测 headerView 的 Y 轴移动

    nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() 

        @Override
        public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) 
            Log.d(TAG, "onScrollChangeForY - scrollY: " + scrollY + " oldScrollY: " + oldScrollY);

            int MOVE = -1, SCROLL_UP = 0, SCROLL_DOWN = 1;
            float initialPositionY = headerView.getY();

            MOVE = scrollY > oldScrollY ? SCROLL_UP : SCROLL_DOWN;

            if (MOVE == SCROLL_UP) 

                int incrementY = scrollY - oldScrollY;

                headerView.setY(initialPositionY - incrementY);

             else 

                int incrementY = oldScrollY - scrollY;

                headerView.setY(initialPositionY + incrementY);
            
        
    );

【讨论】:

【参考方案9】:

有ready for use component,它有助于监听Android中任意视图的滚动事件。在内部,此组件在具有旧 Android API 的设备上添加 ViewTreeObserver 滚动事件侦听器(类似于 Shubhadeep Chaudhuri 的回答中提出的建议),并在具有新 Android API(API 级别 23+)的设备上添加 View 滚动事件侦听器。

【讨论】:

【参考方案10】:

只需使用NestedScrollNestedScroll.setOnScrollChangeListener()

【讨论】:

【参考方案11】:

从 API 23 Android M 开始,您可以使用 OnScrollChangeListener。

 scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() 
        @Override
        public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) 
            //work with parameters
        
    );

【讨论】:

以上是关于Android 滚动视图 onScrollChanged的主要内容,如果未能解决你的问题,请参考以下文章

滚动视图中的 Android 列表视图

Android:禁用列表视图内的元素滚动

Android10.5 滚动视图(RecyclerView)

Android视图之滚动视图

滚动视图内的Android约束布局高度无效

如何在android中滚动视图?