0

I am trying to get a ListView to realign after every scroll/fling. By realign I mean realign in such a way, that the top item of the ListView is aligned with the top of the ListView, if it is cut off it should scroll down smoothly until it is realigned and fully visible.

I implemented a scroll-listener:

firstRowListView.setOnScrollListener(new OnScrollListener() {
        private boolean correcting = false;

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        }

        @Override
        public void onScrollStateChanged(AbsListView arg0, int scrollState) {
                if (!correcting && scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
                    correcting = true;
                    firstRowListView.smoothScrollToPosition(firstRowListView.getFirstVisiblePosition());
                    correcting = false;
                }
            }
        }
    });

(For easier visibility I only left the important bits in). If I scroll smoothly (no fling) it works fine, but if I fling the list it doesn't realign itself. Although LogCat tells me that the onScrollStateChange-method is executed in the same way as when I perform a "normal" scroll.

Why is this and how do I get the ListView to realign even after a Fling?

A. Steenbergen
  • 3,360
  • 4
  • 34
  • 51

2 Answers2

1

Following should work but on Galaxy tab 7 (4.0.4) I can see recursion happening. So I would strongly suggest that you implement some mechanism to avoid that otherwise this solution will break on some devices:

mylv.setOnScrollListener(new OnScrollListener() { 
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) {
        }

        @Override
        public void onScrollStateChanged(final AbsListView lv,
                int scrollState) { 
            if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) { 
                lv.post(new Runnable() {

                    @Override
                    public void run() {
                        lv.smoothScrollToPosition(lv
                                .getFirstVisiblePosition());  
                    }
                }); 
            }
        }
    });
M-Wajeeh
  • 17,204
  • 10
  • 66
  • 103
  • Apparently on on Galaxy tab 7 (4.0.4) after a call to `lv.smoothScrollToPosition(lv.getFirstVisiblePosition())`, `ListView` changes its first visible position by -1 (which should not happen) and subsequent call to `lv.smoothScrollToPosition(lv.getFirstVisiblePosition())` scrolls `ListView` upward and final effect is that `ListView` keeps scrolling upwards till first item is visible. It is definitely a bug. – M-Wajeeh Dec 26 '12 at 17:02
0

On older devices ListView does not report SCROLL_STATE_IDLE after SCROLL_STATE_TOUCH_SCROLL. Please use workaround mentioned here . http://code.google.com/p/android/issues/detail?id=5086#c7

You can also remove correcting variable safely, as it is of no use in this code.

M-Wajeeh
  • 17,204
  • 10
  • 66
  • 103
  • That is not the problem, After SCROLL_STATE_TOUCH_SCROLL the code works fine. Only after SCROLL_STATE_FLING the code does not work. Also, removing the correcting variable results in a stack overflow since appearently smoothScrollTo triggers a change in the scrollstate aswell resulting in an infinite loop. – A. Steenbergen Dec 26 '12 at 14:56
  • Oh I see, then u can keep correcting variable to avoid recursion effect. It seems that after a fling SCROLL_STATE_IDLE is not reported. Have u tried http://stackoverflow.com/questions/1768391/how-to-detect-android-listview-scrolling-stopped – M-Wajeeh Dec 26 '12 at 15:03
  • I just added a LogCat output above smoothScrollTo to check if the scrollState changes to Idle after a fling and a normal touch scroll. It does report a SCROLL_STATE_IDLE after both of them, so smoothScrollTo is executed after a touch scroll and after a fling correctly. The error is though, that the list only scrolls after a touch scroll and after a fling it does not react. The answer you posted is not about my error, since the scrollState is reported correctly, only the list doesnt scroll after a fling. Can the fling runnable interfere with smoothScrollTo? How does the fling work internaly? – A. Steenbergen Dec 26 '12 at 15:12
  • 1
    I just actually tested your code and it is working perfectly on flings too... Android 4.1.2 – M-Wajeeh Dec 26 '12 at 15:25
  • Ah really? Thanks for testing! Maybe it is because of my rooted system (2.3.7). I will test it in an Emulator. I hope it is not a problem of Gingerbread in general – A. Steenbergen Dec 26 '12 at 15:27
  • Does not work in Gingerbread 2.3.3 on an Emulator, seems to be an Android bug then.. – A. Steenbergen Dec 26 '12 at 15:34
  • 1
    Should work now, I tested it on 2.2, no `correcting`, Do tell if it works. `myList.setOnScrollListener(new OnScrollListener() { @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } @Override public void onScrollStateChanged(final AbsListView lv, int scrollState) { if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) { lv.post(new Runnable() { @Override public void run() { lv.smoothScrollToPosition(lv .getFirstVisiblePosition()); } }); } } });` – M-Wajeeh Dec 26 '12 at 16:15
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/21728/discussion-between-m-wajeeh-and-django) – M-Wajeeh Dec 26 '12 at 16:22
  • That solution works perfectly. If you submit this as an answer I can accept it as a solution. Thank you for your help! – A. Steenbergen Dec 26 '12 at 16:39