检测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),否则当视图滚动到范围的顶部或底部时,可能会导致检测滚动方向的问题。
我们应该总是添加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(...)
但是你必须提供你的类添加这个监听器的标志。