Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

smoothScrollToPosition() only scrolls partway in Android ICS?

In Gingerbread, I had no issues with using smoothScrollToPosition() to scroll across dozens of items at a time. After my Nexus S was upgraded to Ice Cream Sandwich, I noticed that no matter what I put in smoothScrollToPosition(), it will only scroll about a couple of hundred pixels in either direction, and then stop.

Is this a known issue with ICS? I've noticed this with the Galaxy Nexus as well. I've looked at a few other questions and tried a few different tricks, such as turning off calls to notifyDataSetChanged(), and posting the smoothScrollToPosition() as a delayed runnable, but unfortunately, it doesn't want to scroll more than a hundred pixels or so before stopping. :(

like image 960
Learn OpenGL ES Avatar asked Jul 04 '12 18:07

Learn OpenGL ES


2 Answers

Appears to be an issue with the duration required to finish the animation, the same issue is present with smoothScrollBy(int distance, int duration), at cursory glance, smoothScrollToPosition() is a friendly wrapper around smoothScrollBy() that does a lot of the legwork. smoothScrollBy() in turn is faking a "fling gesture", as if a user had made the motion.

smoothScrollBy really just posts the fling runnable that continues to repost itself till the duration runs out. Meaning that it simply computes the scroll offset required based on the offset it previously decided to move to, hence if duration runs out before it gets to the target offset, it stops at the last offset calculated. (Rather than all of sudden jumping to the target offset, which is perhaps more jarring as it would not be animated).

The difficulty for the Android guys is determining how much to move by each run() call to reach the required offset because ListView cells (children) are entirely dynamic in height, so they can't just do a simple distance calculation as the non-visible children's height are unknown to them. It is the same reason the Android scrollbar can fluctuate in size as you scroll, it has to take a best guess at how big it should be based on what it is currently seeing.

Anyway that doesn't help you solve it but some one might find it interesting :)

If you know you have static cell heights however, you can write your own method to calculate the distance and duration to pass to smoothScrollBy() yourself and have a static amount of time to move X distance. If you don't, it will have to suffice to use the solution bigstones posted, which really is working because of the high SCROLL_DURATION of 1000ms. You can take the ICS version and change this attribute as well, rather than using the 2.2 version, which is not the root cause.

You can also adapt those runnables with your own custom algorithm, it shouldn't be too difficult to tweak things.

like image 78
Grge Monkey Avatar answered Nov 15 '22 09:11

Grge Monkey


This is what I did to resolve the issue. Essentially, we want to keep calling smoothscrolling until you reach the desired element (in this case, I simply want to scroll to the top, which is element 0).

//There is a known bug where smoothScrollToPosition(..) may not reach the top, 
            //if the list is very large, keep scrolling until you reach the top element
            newsFeed.setOnScrollListener(new OnScrollListener() {
                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) {

                }

                @Override
                public void onScroll(AbsListView view, int firstVisibleItem,
                        int visibleItemCount, int totalItemCount) {
                    if(firstVisibleItem != 0) {
                        newsFeed.smoothScrollToPosition(0);
                    } 
                    else {
                        //We are done
                        newsFeed.setOnScrollListener(null);
                    }
                }
            });
            newsFeed.smoothScrollToPosition(0);
like image 40
iko_wp Avatar answered Nov 15 '22 09:11

iko_wp