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

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

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

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

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

【问题讨论】:

标签: android android-scrollview onscrollchanged


【解决方案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 手动执行此操作,这非常烦人。以下是遇到此问题的任何人的一些建议:stackoverflow.com/questions/8181828/…
【解决方案3】:

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

API 23 开始,Android 的 View 有一个 OnScrollChangeListenermatching setter

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

【讨论】:

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

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

【讨论】:

  • 我在公司的一个项目中使用了这个解决方案。在我在这里发布一些东西之前,我会测试一切。也许你也应该这样做?
  • 抱歉,误读了问题。它适用于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
                      }
                  });
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-03-04
                • 2015-08-10
                • 1970-01-01
                • 2014-04-20
                相关资源
                最近更新 更多