Search code examples
androidlistviewanimationandroid-scrollview

Scroll ListView from bottom to top


Have a ListView and when the activity starts I want it to scroll from the bottom to the top. I can get this working by setting myListView.setStackFromBottom(true) in onCreate so the list is at the bottom when the activity loads.

Then I override onWindowFocusChanged and use smoothScrollToPosition(0) which will scroll the list to the top. However, I need the scroll speed to gradually slow down as it comes to the top, similar to what a fling looks like. Is there any way to do this with an animation or another way?

Thanks.


Solution

  • You can write your own scroller by using CountDownTimer.

    import android.content.Context;
    import android.os.CountDownTimer;
    import android.util.AttributeSet;
    import android.view.KeyEvent;
    import android.view.MotionEvent;
    import android.view.animation.AnticipateOvershootInterpolator;
    import android.view.animation.Interpolator;
    import android.widget.ScrollView;
    
    public class SmoothScrollView extends ScrollView {
    
        private static final long SCROLL_DURATION = 1500; //milliseconds
        //interpolator for scroller
        private static final Interpolator INTERPOLATOR = new AnticipateOvershootInterpolator(1);
    
        private SmoothScroller smoothScroller;
    
    
        public SmoothScrollView(Context context) {
            this(context, null, 0);
        }
    
    
        public SmoothScrollView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
    
        public SmoothScrollView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            setSmoothScrollingEnabled(true);
        }
    
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            if (smoothScroller != null)//we are scrolling
                return true;
            else return super.onTouchEvent(ev);
        }
    
        @Override
        public boolean executeKeyEvent(KeyEvent ev) {
            if (smoothScroller != null)//we are scrolling
                return true;
            else return super.executeKeyEvent(ev);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            if (smoothScroller != null)//we are scrolling
                return true;
            else return super.onInterceptTouchEvent(ev);
        }
    
        public void smoothScrollTo(int scrollX, int scrollY) {
            if (smoothScroller != null) {
                smoothScroller.cancel();
            }
            int deltaY = scrollY - getScrollY();
        int deltaX = scrollX - getScrollX();
            smoothScroller = new SmoothScroller(SCROLL_DURATION, getScrollX(), getScrollY(), deltaX, deltaY);
            smoothScroller.start();
        }
    
        private class SmoothScroller extends CountDownTimer {
    
            private int fromX;
            private int fromY;
            private int deltaX;
            private int deltaY;
            private float scrollTime;
    
            public SmoothScroller(long scrollTime, int fromX, int fromY, int deltaX, int deltaY) {
                super(scrollTime, 1);
                this.scrollTime = scrollTime;
                this.fromX = fromX;
                this.fromY = fromY;
                this.deltaX = deltaX;
                this.deltaY = deltaY;
            }
    
            @Override
            public void onTick(long millisUntilFinished) {
                    float delta = (scrollTime - millisUntilFinished) / scrollTime;
                    delta = INTERPOLATOR.getInterpolation(delta);
                    int x = fromX + ((int) (delta * deltaX));
                    int y = fromY + ((int) (delta * deltaY));
                    smoothScrollTo(x, y);
            }
    
            @Override
            public void onFinish() {
                    float delta = 1f;
                    int x = fromX + ((int) (delta * deltaX));
                    int y = fromY + ((int) (delta * deltaY));
                    smoothScroller = null;
                    scrollTo(x, y);
            }
        }
    }
    

    This is what I use. Just change the INTERPOLATOR and SCROLL_DURATION based on your needs and call smoothScrollTo instead scrollTo.

    I am pretty sure changing ScrollView to ListView won`t cause any problems.