可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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 page where is explained. But, in the example, that guys extends ScrollView
. As I said, I need to extend 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\".
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?
Thank you people.
EDIT:
As promised, here is the piece of my XML that matters:
<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
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...
Thanks for your answers!
回答1:
Did it!
Aside of the fix Alexandre kindly provide me, I had to create an Interface:
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:
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:
scroll = (ScrollViewExt) findViewById(R.id.scrollView1);
scroll.setScrollViewListener(this);
And in the method OnScrollChanged, from the interface...
@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!
回答2:
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_parent or wrap_content.
回答3:
All of these answers are so complicated, but there is a simple built-in method that accomplishes this: 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
.
If you have a horizontal ScrollView, the same can be achieved with canScrollHorizontally(int)
回答4:
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:
@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
}
}
回答5:
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.
<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 ?
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
}
}
});
回答6:
You can make use of the Support Library\'s NestedScrollView
and it\'s NestedScrollView.OnScrollChangeListener
interface.
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
:
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.
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.
https://developer.android.com/reference/android/view/View.html#setOnScrollChangeListener(android.view.View.OnScrollChangeListener)
回答7:
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.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
}
}
}
});
回答8:
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 scrollPositionChanged that checks if scroll position even changed before calling method once again.
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.
回答9:
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
}
}
});
回答10:
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.
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;
}