Android 检测 ScrollView 的结束
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10316743/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Detect end of ScrollView
提问by Fustigador
I have an app, that has an Activity that uses a ScrollView
. I need to detect when user gets to the bottom of the ScrollView
. I did some googleing and I found this pagewhere is explained. But, in the example, that guys extends ScrollView
. As I said, I need to extend Activity.
我有一个应用程序,它有一个使用ScrollView
. 我需要检测用户何时到达ScrollView
. 我做了一些谷歌搜索,我发现这个页面有解释。但是,在这个例子中,那些家伙扩展了ScrollView
. 正如我所说,我需要扩展 Activity。
So, I said "ok, let's try to make a custom class extending ScrollView
, override the onScrollChanged()
method, detect the end of the scroll, and act accordingly".
所以,我说“好吧,让我们尝试制作一个自定义类扩展ScrollView
,覆盖该onScrollChanged()
方法,检测滚动的结尾,并采取相应的行动”。
I did, but in this line:
我做了,但在这一行:
scroll = (ScrollViewExt) findViewById(R.id.scrollView1);
it throws a java.lang.ClassCastException
. I changed the <ScrollView>
tags in my XML but, obviously, it doesn't work. My questions are: Why, if ScrollViewExt
extends ScrollView
, throws to my face a ClassCastException
? is there any way to detect end of scrolling without messing too much?
它抛出一个 java.lang.ClassCastException
. 我更改了<ScrollView>
XML 中的标签,但很明显,它不起作用。我的问题是:为什么,如果ScrollViewExt
extends ScrollView
,扔到我的脸上ClassCastException
?有没有办法检测滚动结束而不会弄乱太多?
Thank you people.
谢谢你们。
EDIT: As promised, here is the piece of my XML that matters:
编辑:正如所承诺的,这是我的 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>
I changed it from TextView
to WebView
to be able of justifying the text inside. What i want to achieve is the "Accept button doesn't activate until the terms of the contract are fully read" thing. My extended class is called ScrollViewEx
t. If i change the tag ScrollView
for ScrollViewExt
it throws an
我将它从TextView
改为WebView
以便能够证明里面的文字是合理的。我想要实现的是“在完全阅读合同条款之前不会激活接受按钮”的事情。我的扩展课程称为ScrollViewEx
t。如果我更改标签ScrollView
,ScrollViewExt
它会抛出一个
android.view.InflateException: Binary XML file line #44: Error inflating class ScrollViewExt
because it doesn't understand the tag ScrollViewEx
. I don't think it has a solution...
因为它不理解标签ScrollViewEx
。我不认为它有解决方案......
Thanks for your answers!
感谢您的回答!
回答by Fustigador
Did it!
做到了!
Aside of the fix Alexandre kindly provide me, I had to create an Interface:
除了 Alexandre 为我提供的修复程序之外,我还必须创建一个接口:
public interface ScrollViewListener {
void onScrollChanged(ScrollViewExt scrollView,
int x, int y, int oldx, int oldy);
}
Then, i had to override the OnScrollChanged method from ScrollView in my ScrollViewExt:
然后,我必须在我的 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);
}
}
}
Now, as Alexandre said, put the package name in the XML tag (my fault), make my Activity class implement the interface created before, and then, put it all together:
现在,正如Alexandre所说,把包名放在XML标签中(我的错),让我的Activity类实现之前创建的接口,然后,把它们放在一起:
scroll = (ScrollViewExt) findViewById(R.id.scrollView1);
scroll.setScrollViewListener(this);
And in the method OnScrollChanged, from the interface...
在方法 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
}
}
And it worked!
它奏效了!
Thank you very much for your help, Alexandre!
非常感谢您的帮助,亚历山大!
回答by Think Twice Code Once
I found a simple way to detect this :
我找到了一个简单的方法来检测这个:
scrollView.getViewTreeObserver()
.addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
if (scrollView.getChildAt(0).getBottom()
<= (scrollView.getHeight() + scrollView.getScrollY())) {
//scroll view is at bottom
} else {
//scroll view is not at bottom
}
}
});
- Doesn't need to custom ScrollView.
- Scrollview can host only one direct child, so scrollView.getChildAt(0)is okay.
- This solution is right even the height of direct child of scroll view is match_parentor wrap_content.
- 不需要自定义 ScrollView。
- Scrollview 只能承载一个直接子级,因此scrollView.getChildAt(0)没问题。
- 即使滚动视图的直接子项的高度是match_parent或wrap_content ,此解决方案也是正确的。
回答by yuval
All of these answers are so complicated, but there is a simple built-in method that accomplishes this: canScrollVertically(int)
所有这些答案都非常复杂,但有一个简单的内置方法可以实现这一点: canScrollVertically(int)
For example:
例如:
@Override
public void onScrollChanged() {
if (!scrollView.canScrollVertically(1)) {
// bottom of scroll view
}
if (!scrollView.canScrollVertically(-1)) {
// top of scroll view
}
}
This also works with RecyclerView, ListView, and actually any other view since the method is implemented on View
.
这也适用于 RecyclerView、ListView 和实际上任何其他视图,因为该方法是在View
.
If you have a horizontal ScrollView, the same can be achieved with canScrollHorizontally(int)
如果你有一个水平滚动视图,同样可以实现 canScrollHorizontally(int)
回答by Will
Fustigador answer was great, but I found some device (Like Samsung Galaxy Note V) cannot reach 0, have 2 point left, after the calculation. I suggest to add a little buffer like below:
Fustigador 的回答很棒,但我发现有些设备(如三星 Galaxy Note V)无法达到 0,计算后还剩 2 点。我建议添加一个小缓冲区,如下所示:
@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
}
}
回答by Alexandre B.
EDIT
编辑
With the content of your XML, I can see that you use a ScrollView. If you want to use your custom view, you must write com.your.packagename.ScrollViewExt and you will be able to use it in your code.
通过 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>
EDIT END
编辑结束
Could you post the xml content ?
你能发布xml内容吗?
I think that you could simply add a scroll listener and check if the last item showed is the lastest one from the listview like :
我认为您可以简单地添加一个滚动侦听器并检查显示的最后一项是否是列表视图中的最新一项,例如:
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
}
}
});
回答by Shweta Chandarana
scrollView = (ScrollView) findViewById(R.id.scrollView);
scrollView.getViewTreeObserver()
.addOnScrollChangedListener(new
ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
if (!scrollView.canScrollVertically(1)) {
// bottom of scroll view
}
if (!scrollView.canScrollVertically(-1)) {
// top of scroll view
}
}
});
回答by TheIT
You can make use of the Support Library's NestedScrollView
and it's NestedScrollView.OnScrollChangeListener
interface.
您可以使用支持库NestedScrollView
及其NestedScrollView.OnScrollChangeListener
界面。
https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html
https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html
Alternatively if your app is targeting API 23 or above, you can make use of the following method on the ScrollView
:
或者,如果您的应用面向 API 23 或更高版本,您可以在 上使用以下方法ScrollView
:
View.setOnScrollChangeListener(OnScrollChangeListener listener)
Then follow the example that @Fustigador described in his answer. Note however that as @Will described, you should consider adding a small buffer in case the user or system isn't able to reach the complete bottom of the list for any reason.
然后按照@Fustigador 在他的回答中描述的示例进行操作。但是请注意,正如@Will 所述,您应该考虑添加一个小缓冲区,以防用户或系统因任何原因无法到达列表的完整底部。
Also worth noting is that the scroll change listener will sometimes be called with negative values or values greater than the view height. Presumably these values represent the 'momentum' of the scroll action. However unless handled appropriately (floor / abs) they can cause problems detecting the scroll direction when the view is scrolled to the top or bottom of the range.
另外值得注意的是,滚动更改侦听器有时会以负值或大于视图高度的值被调用。大概这些值代表滚动动作的“动量”。但是,除非处理得当(地板/绝对),否则当视图滚动到范围的顶部或底部时,它们可能会导致检测滚动方向的问题。
回答by Pranav
We should always add scrollView.getPaddingBottom()
to match full scrollview height because some time scroll view has padding in xml file so that case its not going to work.
我们应该始终添加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
}
}
}
});
回答by Phatee P
Most of answers works beside a fact, that when u scroll to the bottom, listener is triggered several times, which in my case is undesirable. To avoid this behavior I've added flag scrollPositionChangedthat checks if scroll position even changed before calling method once again.
大多数答案都适用于一个事实,即当您滚动到底部时,会多次触发侦听器,这在我的情况下是不可取的。为了避免这种行为,我添加了标志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;
}
}
Then just set listener
然后只需设置侦听器
scrollView.setScrollEndingListener(new EndDetectingScrollView.ScrollEndingListener() {
@Override
public void onScrolledToEnd() {
//do your stuff here
}
});
You may do the same thing if u do in like
如果你喜欢,你可以做同样的事情
scrollView.getViewTreeObserver().addOnScrollChangedListener(...)
but you have to provide flag from class your adding this listener.
但是您必须从添加此侦听器的类中提供标志。
回答by Willi
To determine if you are at the end of your custom ScrollView
you could also use a member variable and store the last y-position. Then you can compare the last y-position with the current scroll position.
要确定您是否在自定义的末尾,您ScrollView
还可以使用成员变量并存储最后的 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;
}