Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

smoothScrollToPositionFromTop() is not always working like it should

Tags:

I've been trying for a while to get smoothScrollToPositionFromTop() working, but it doesn't always scroll to the correct position.

I've got a ListView (with 10 items) in a layout with 10 buttons on the side, so I can scroll to every item in the list. Usually when I scroll one position back or forward it works fine, but often when I try to scroll more then 3 positions back or forward the ListView does not exactly end at the selected position. When it fails, it usually ends up 0,5 to 1,5 items off and it is not really predictable when the scroll fails.

I have also checked out smoothScrollToPosition after notifyDataSetChanged not working in android, but this fix is not working for me and I don't change any data.

I would really like to automatically scroll to the selected listitems, but not I can't figure out how. Has anybody had this problem before and knows how to fix it?

like image 383
mennovogel Avatar asked Jan 23 '13 11:01

mennovogel


2 Answers

This is a known bug. See https://code.google.com/p/android/issues/detail?id=36062

However, I implemented this workaround that deals with all edge cases that might occur:

First call smothScrollToPositionFromTop(position) and then, when scrolling has finished, call setSelection(position). The latter call corrects the incomplete scrolling by jumping directly to the desired position. Doing so the user still has the impression that it is being animation-scrolled to this position.

I implemented this workaround within two helper methods:

smoothScrollToPositionFromTop()

public static void smoothScrollToPositionFromTop(final AbsListView view, final int position) {     View child = getChildAtPosition(view, position);     // There's no need to scroll if child is already at top or view is already scrolled to its end     if ((child != null) && ((child.getTop() == 0) || ((child.getTop() > 0) && !view.canScrollVertically(1)))) {         return;     }      view.setOnScrollListener(new AbsListView.OnScrollListener() {         @Override         public void onScrollStateChanged(final AbsListView view, final int scrollState) {             if (scrollState == SCROLL_STATE_IDLE) {                 view.setOnScrollListener(null);                  // Fix for scrolling bug                 new Handler().post(new Runnable() {                     @Override                     public void run() {                         view.setSelection(position);                     }                 });             }         }          @Override         public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount,                                  final int totalItemCount) { }     });      // Perform scrolling to position     new Handler().post(new Runnable() {         @Override         public void run() {             view.smoothScrollToPositionFromTop(position, 0);         }     }); } 

getChildAtPosition()

public static View getChildAtPosition(final AdapterView view, final int position) {     final int index = position - view.getFirstVisiblePosition();     if ((index >= 0) && (index < view.getChildCount())) {         return view.getChildAt(index);     } else {         return null;     } } 
like image 147
Lars Blumberg Avatar answered Sep 23 '22 03:09

Lars Blumberg


Here is an implementation of the solution.

    void smoothScrollToPositionFromTopWithBugWorkAround(final AbsListView listView,                                                     final int position,                                                     final int offset,                                                      final int duration){      //the bug workaround involves listening to when it has finished scrolling, and then      //firing a new scroll to the same position.      //the bug is the case that sometimes smooth Scroll To Position sort of misses its intended position.      //more info here : https://code.google.com/p/android/issues/detail?id=36062     listView.smoothScrollToPositionFromTop(position, offset, duration);     listView.setOnScrollListener(new OnScrollListener() {          @Override         public void onScrollStateChanged(AbsListView view, int scrollState) {             if(scrollState==OnScrollListener.SCROLL_STATE_IDLE){                 listView.setOnScrollListener(null);                 listView.smoothScrollToPositionFromTop(position, offset, duration);             }          }          @Override         public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {         }     }); } 
like image 22
havchr Avatar answered Sep 25 '22 03:09

havchr