Search code examples
androidscrollandroid-listviewsmooth-scrolling

How to smooth the movement of a list item at a specific position


I have listView. When item is in certain position he changes color. (see picture).On the top(with blue squares) - it is what I have now. Down - what I need. But when scrolling, when the item is in the desired position and changes color, it does not happen smoothly. The item seems to jump to the desired position. How can I make this process smoother? Thanks.

here is the part of my view class:

public class TimeView extends View {
...........................

    @Override
    public void setOnScrollChangeListener(OnScrollChangeListener l) {
    }

    AbsListView.OnScrollListener scrollListener = new AbsListView.OnScrollListener() {

        @Override
        public void onScrollStateChanged(final AbsListView view, int scrollState) {
            if (scrollState == SCROLL_STATE_IDLE) {
                View childView = view.getChildAt(0);
                int position = view.getFirstVisiblePosition();
                if (-childView.getTop() > childView.getHeight() / 2) {
                    position++;
                }
             
              view.setSelection(position);
               }
        }

        @Override
        public void onScroll(final AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            for (int i = 0; i < view.getChildCount(); i++) {
                View childView = view.getChildAt(i);
                if (i == 1) {
                    childView.setBackgroundResource(R.drawable.selected_color);

                } else {
                    childView.setBackgroundResource(R.drawable.no_select_color);
                }
            }
        }

    };

Solution

  • Try this:

        @Override
        public void onScrollStateChanged(final AbsListView view, int scrollState) {
            if (scrollState == SCROLL_STATE_IDLE) {
                View firstView = view.getChildAt(0);
                Rect rect = new Rect(0, 0, firstView.getWidth(), firstView.getHeight());
                view.getChildVisibleRect(firstView, rect, null);
                int position = view.getFirstVisiblePosition();
                if (rect.height() < (firstView.getHeight() / 2)) {
                    position++;
                }
                final int finalPosition = position;
                view.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        view.smoothScrollToPositionFromTop(finalPosition, 0, 500);
                    }
                }, 1);
            }
        }
    

    Updated: Above code has a constant settling time (500ms). Below code has a constant snap speed (0.2 pixel/ms, it means scroll up/down a pixel takes 5ms).

        final static int SCROLL_TIME_PER_PIXEL = 5; // Time(ms) to snap a pixel.
        @Override
        public void onScrollStateChanged(final AbsListView view, int scrollState) {
            if (scrollState == SCROLL_STATE_IDLE) {
                View firstView = view.getChildAt(0);
                Rect rect = new Rect(0, 0, firstView.getWidth(), firstView.getHeight());
                view.getChildVisibleRect(firstView, rect, null);
                int position = view.getFirstVisiblePosition();
                int offset = rect.height();
                if (rect.height() < (firstView.getHeight() / 2)) {
                    position++;
                } else offset = firstView.getHeight() - rect.height();
                final int finalPosition = position; // Snap to position.
                final int finalOffset = offset; // Snap distance in pixels.
                view.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        view.smoothScrollToPositionFromTop(finalPosition, 0, finalOffset * SCROLL_TIME_PER_PIXEL);
                    }
                }, 1);
            }
        }