检测ScrollView的结束

我有一个应用程序,它有一个使用ScrollView的活动。 我需要检测用户何时到达ScrollView的底部。 我做了一些googleing,我发现这个网页在哪里解释。 但是,在这个例子中,这个家伙扩展了ScrollView 。 正如我所说的,我需要扩展活动。

所以,我说:“好吧,让我们试着做一个扩展ScrollView的自定义类,重写onScrollChanged()方法,检测滚动结束,并相应地采取行动。

我做了,但在这一行:

 scroll = (ScrollViewExt) findViewById(R.id.scrollView1); 

它抛出java.lang.ClassCastException 。 我改变了我的XML中的<ScrollView>标签,但显然,它不起作用。 我的问题是:为什么,如果ScrollViewExt扩展ScrollView ,抛出我的脸一个ClassCastException ? 有没有什么办法来检测滚动结束,而不是太多?

谢谢你们

编辑:如所承诺的,这是我的XML的重要事项:

 <ScrollView android:id="@+id/scrollView1" android:layout_width="match_parent" android:layout_height="match_parent" > <WebView android:id="@+id/textterms" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" android:textColor="@android:color/black" /> </ScrollView> 

我将其从TextView更改为WebView能够certificate里面的文本。 我想达到的是“接受button不激活,直到合同条款被完全读取”的事情。 我的扩展类叫做ScrollViewEx t。 如果我更改标签ScrollView ScrollViewExt它会引发

 android.view.InflateException: Binary XML file line #44: Error inflating class ScrollViewExt 

因为它不理解标签ScrollViewEx 。 我不认为它有一个解决scheme…

感谢您的回答!

做到了!

除了亚历山大提供给我的信息,我不得不创build一个接口:

 public interface ScrollViewListener { void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy); } 

然后,我不得不从我的ScrollViewExt中的ScrollView重写OnScrollChanged方法:

 public class ScrollViewExt extends ScrollView { private ScrollViewListener scrollViewListener = null; public ScrollViewExt(Context context) { super(context); } public ScrollViewExt(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public ScrollViewExt(Context context, AttributeSet attrs) { super(context, attrs); } public void setScrollViewListener(ScrollViewListener scrollViewListener) { this.scrollViewListener = scrollViewListener; } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (scrollViewListener != null) { scrollViewListener.onScrollChanged(this, l, t, oldl, oldt); } } } 

现在,正如Alexandre所说,把包名称放在XML标签(我的错)中,让我的Activity类实现之前创build的接口,然后把它放在一起:

 scroll = (ScrollViewExt) findViewById(R.id.scrollView1); scroll.setScrollViewListener(this); 

在方法OnScrollChanged,从接口…

 @Override public void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy) { // We take the last son in the scrollview View view = (View) scrollView.getChildAt(scrollView.getChildCount() - 1); int diff = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY())); // if diff is zero, then the bottom has been reached if (diff == 0) { // do stuff } } 

它的工作!

亚历山大,非常感谢你的帮助!

Fustigador的回答很棒,但是我发现有些设备(如Samsung Galaxy Note V)不能达到0,计算后还剩2分。 我build议像下面添加一个小缓冲区:

 @Override public void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy) { // We take the last son in the scrollview View view = (View) scrollView.getChildAt(scrollView.getChildCount() - 1); int diff = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY())); // if diff is zero, then the bottom has been reached if (diff <= 10) { // do stuff } } 

我发现了一个简单的方法来检测这个:

 scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() { @Override public void onScrollChanged() { if (scrollView != null) { if (scrollView.getChildAt(0).getBottom() <= (scrollView.getHeight() + scrollView.getScrollY())) { //scroll view is at bottom } else { //scroll view is not at bottom } } } }); 
  • 不需要自定义ScrollView。
  • ScrollView只能托pipe一个直接的孩子,所以scrollView.getChildAt(0)没问题。
  • 这个解决scheme对即使scroll view的直接子的高度是match_parent或者wrap_content

编辑

随着你的XML的内容,我可以看到你使用ScrollView。 如果要使用自定义视图,则必须编写com.your.packagename.ScrollViewExt,并且可以在代码中使用它。

 <com.your.packagename.ScrollViewExt android:id="@+id/scrollView1" android:layout_width="match_parent" android:layout_height="match_parent" > <WebView android:id="@+id/textterms" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" android:textColor="@android:color/black" /> </com.your.packagename.ScrollViewExt> 

编辑结束

你可以发布的XML内容?

我认为你可以简单地添加一个滚动监听器,并检查最后显示的项目是否是从列表视图中最新的一个:

 mListView.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // TODO Auto-generated method stub } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // TODO Auto-generated method stub if(view.getLastVisiblePosition()==(totalItemCount-1)){ //dosomething } } }); 

您可以使用支持库的NestedScrollView和它的NestedScrollView.OnScrollChangeListener接口。

https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html

或者,如果您的应用的目标是API 23或更高版本,则可以在ScrollView上使用以下方法:

 View.setOnScrollChangeListener(OnScrollChangeListener listener) 

然后按照@Fustigador在他的回答中描述的例子。 但是请注意,正如@Will所描述的那样,您应该考虑添加一个小缓冲区,以防用户或系统出于任何原因无法到达列表的完整底部。

另外值得注意的是,滚动更改侦听器有时会被调用负值或大于视图高度的值。 推测这些值代表了滚动动作的“动量”。 但是,除非正确处理(floor / abs),否则当视图滚动到范围的顶部或底部时,可能会导致检测滚动方向的问题。

https://developer.android.com/reference/android/view/View.html#setOnScrollChangeListener(android.view.View.OnScrollChangeListener);

我们应该总是添加scrollView.getPaddingBottom()来匹配完整的滚动视图高度,因为一些时间滚动视图在xml文件中有填充,所以这种情况下,它不会工作。

 scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() { @Override public void onScrollChanged() { if (scrollView != null) { View view = scrollView.getChildAt(scrollView.getChildCount()-1); int diff = (view.getBottom()+scrollView.getPaddingBottom()-(scrollView.getHeight()+scrollView.getScrollY())); // if diff is zero, then the bottom has been reached if (diff == 0) { // do stuff } } } }); 

要确定您是否在自定义ScrollView的末尾,您还可以使用成员variables并存储最后的y位置。 然后你可以比较最后的y位置和当前的滚动位置。

 private int scrollViewPos; ... @Override public void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy) { //reached end of scrollview if (y > 0 && scrollViewPos == y){ //do something } scrollViewPos = y; } 

大部分答案的作用是旁边的一个事实,当你滚动到底部,有时代码被调用了几次 ,这在我的情况是不可取的 。 为了避免这种行为,我添加了标志scrollPositionChanged ,检查滚动位置是否更改再次调用方法之前。

 public class EndDetectingScrollView extends ScrollView { private boolean scrollPositionChanged = true; private ScrollEndingListener scrollEndingListener; public interface ScrollEndingListener { void onScrolledToEnd(); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); View view = this.getChildAt(this.getChildCount() - 1); int diff = (view.getBottom() - (this.getHeight() + this.getScrollY())); if (diff <= 0) { if (scrollPositionChanged) { scrollPositionChanged = false; if (scrollEndingListener != null) { scrollEndingListener.onScrolledToEnd(); } } } else { scrollPositionChanged = true; } } public void setScrollEndingListener(ScrollEndingListener scrollEndingListener) { this.scrollEndingListener = scrollEndingListener; } } 

然后只需设置监听器

 scrollView.setScrollEndingListener(new EndDetectingScrollView.ScrollEndingListener() { @Override public void onScrolledToEnd() { //do your stuff here } }); 

如果你喜欢,你可以做同样的事情

 scrollView.getViewTreeObserver().addOnScrollChangedListener(...) 

但是你必须提供你的类添加这个监听器的标志。