Detect ScrollView End

I have an application that has an Activity that uses a ScrollView . I need to determine when the user reaches the bottom of the ScrollView . I did some googleing and I found this page explaining. But in this example, the guys extend the ScrollView . As I said, I need to expand the Activity.

So, I said: "Well, try to make your own class that extends ScrollView , override the onScrollChanged() method, determine the end of the scroll and act accordingly."

I did, but on this line:

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

it throws a java.lang.ClassCastException . I changed the <ScrollView> tags in my XML, but obviously this does not work. My questions are: why, if ScrollViewExt extends ScrollView , throws a ClassCastException in my face? is there any way to detect the end of the scroll without too much noise?

Thank you people.

EDIT: As promised, here is part of my 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 to justify the text inside. What I want to achieve is that the “Accept” button is not activated until the terms of the contract are fully read. "My extended class is called ScrollViewEx t. If I change the ScrollView tag for ScrollViewExt , it will produce

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

because he does not understand the ScrollViewEx tag. I don’t think he has a solution ...

Thank you for your responses!

+64
android android-scrollview
Apr 25 '12 at 13:30
source share
11 answers

Did this!

Besides the fix Alexandre kindly provided 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 mistake), make my Activity class implement the previously created interface, and then put it all together:

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

And in the OnScrollChanged method, 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, Alexander!

+104
Apr 26 '12 at 13:23
source share

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 } } }); 
  • No need to configure ScrollView.
  • Scrollview can contain only one direct child, so scrollView.getChildAt (0) can be used.
  • This is the right solution, even if the height of the direct child of the scroll view is match_parent or wrap_content .
+56
Dec 14 '16 at 10:00
source share

All of these answers are so complex, but there is a simple built-in method that does 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 } } 

It also works with RecyclerView, ListView, and virtually any other view, as the method is implemented in View .

If you have a horizontal ScrollView, you can do the same with canScrollHorizontally(int)

+31
Feb 08 '18 at 19:58
source share

Fustigador's answer was excellent, but I found some device (for example, Samsung Galaxy Note V) cannot reach 0, 2 points are left after calculation. I suggest adding a small buffer as shown 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 } } 
+16
17 Sep '15 at 4:08
source share

EDIT

With the contents of your XML, I see that you are using ScrollView. If you want to use your own view, you must write com.your.packagename.ScrollViewExt and you can 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 you could just add a scroll listener and check if the last shown is the last last from the list:

 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 } } }); 
+10
Apr 25 2018-12-12T00:
source share

You can use the support library interface NestedScrollView and NestedScrollView.OnScrollChangeListener .

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

Otherwise, if your application is for API 23 or higher, you can use the following method in ScrollView :

 View.setOnScrollChangeListener(OnScrollChangeListener listener) 

Then follow the example described by @Fustigador in his answer. Please note, however, that as described in @, you should consider adding a small buffer if the user or the system cannot reach the full bottom of the list for any reason.

It is also worth noting that the scroll change listener is sometimes called with negative values ​​or values ​​that are greater than the height of the view. Presumably, these values ​​represent the “momentum” of the scroll action. However, if they are not handled properly (gender / abs), they can cause problems with detecting the scroll direction when scrolling the view at the top or bottom of the range.

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

+4
07 Oct '16 at 2:35
source share

We should always add scrollView.getPaddingBottom() to match the full scroll height, because for some time the scroll has a registration in the xml file, so the case will not 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 } } } }); 
+3
Dec 20 '16 at 17:33
source share
 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 } } }); 
+3
Oct 12 '18 at 8:25
source share

Most of the answers work besides the fact that when you scroll to the end, the listener starts several times , which in my case is undesirable . To avoid this, I added the scrollPositionChanged flag, which checks to see if even the scroll position has changed before calling the method 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 the listener

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

You can do the same if you do as

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

but you must provide the flag from the class by adding this listener.

+2
Sep 22 '17 at 12:24
source share

To determine if you are at the end of a custom ScrollView , you can also use a member variable and keep the last y position. Then you can compare the last y position with the current scrollbar.

 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; } 
+1
Jul 03 '14 at
source share

I wanted to show / hide the FAB with the offset to the very bottom of the scroll. Here is the solution I came up with (Kotlin):

 scrollview.viewTreeObserver.addOnScrollChangedListener { if (scrollview.scrollY < scrollview.getChildAt(0).bottom - scrollview.height - offset) { // fab.hide() } else { // fab.show() } } 
0
Jul 03 '19 at 13:50
source share



All Articles