Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scroll a ListView by pixels in Android

I want to scroll the a ListView in Android by number of pixels. For example I want to scroll the list 10 pixels down (so that the first item on the list has its top 10 pixel rows hidden).

I thought the obviously visible scrollBy or scrollTo methods on ListView would do the job, but they don't, instead they scroll the whole list wrongly (In fact, the getScrollY always return zero even though I have scrolled the list using my finger.)

What I'm doing is I'm capturing Trackball events and I want to scroll the listview smoothly according to the motion of the trackball.

like image 959
Randy Sugianto 'Yuku' Avatar asked Mar 03 '10 21:03

Randy Sugianto 'Yuku'


4 Answers

The supported way to scroll a ListView widget is:

mListView.smoothScrollToPosition(position);

http://developer.android.com/reference/android/widget/AbsListView.html#smoothScrollToPosition(int)

However since you mentioned specifically that you would like to offset the view vertically, you must call:

mListView.setSelectionFromTop(position, yOffset);

http://developer.android.com/reference/android/widget/ListView.html#setSelectionFromTop(int,%20int)

Note that you can also use smoothScrollByOffset(yOffset). However it is only supported on API >= 11

http://developer.android.com/reference/android/widget/ListView.html#smoothScrollByOffset(int)

like image 51
Sam Avatar answered Sep 22 '22 13:09

Sam


If you look at the source for the scrollListBy() method added in api 19 you will see that you can use the package scoped trackMotionScroll method.

public class FutureListView {

    private final ListView mView;

    public FutureListView(ListView view) {
        mView = view;
    }

    /**
     * Scrolls the list items within the view by a specified number of pixels.
     *
     * @param y the amount of pixels to scroll by vertically
     */
    public void scrollListBy(int y) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            mView.scrollListBy(y);
        } else {
            // scrollListBy just calls trackMotionScroll
            trackMotionScroll(-y, -y);
        }
    }

    private void trackMotionScroll(int deltaY, int incrementalDeltaY) {
        try {
            Method method = AbsListView.class.getDeclaredMethod("trackMotionScroll", int.class, int.class);
            method.setAccessible(true);
            method.invoke(mView, deltaY, incrementalDeltaY);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        };
    }
}
like image 40
enl8enmentnow Avatar answered Sep 21 '22 13:09

enl8enmentnow


Here is some code from my ListView subclass. It can easily be adapted so it can be used in Activity code.

getListItemsHeight() returns the total pixel height of the list, and fills an array with vertical pixel offsets of each item. While this information is valid, getListScrollY() returns the current vertical pixel scroll position, and scrollListToY() scrolls the list to pixel position. If the size or the content of the list changes, getListItemsHeight() has to be called again.

private int m_nItemCount;
private int[] m_nItemOffY;

private int getListItemsHeight() 
{  
    ListAdapter adapter = getAdapter();  
    m_nItemCount = adapter.getCount();
    int height = 0;
    int i;

    m_nItemOffY = new int[m_nItemCount];

    for(i = 0; i< m_nItemCount; ++i){ 
        View view  = adapter.getView(i, null, this);  

        view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));  

        m_nItemOffY[i] = height;
        height += view.getMeasuredHeight();
    }  

    return height;  
}  

private int getListScrollY()
{
    int pos, nScrollY, nItemY;
    View view;

    pos = getFirstVisiblePosition();
    view = getChildAt(0);
    nItemY = view.getTop();
    nScrollY = m_nItemOffY[pos] - nItemY;

    return nScrollY;
}

private void scrollListToY(int nScrollY)
{
    int i, off;

    for(i = 0; i < m_nItemCount; ++i){
        off = m_nItemOffY[i] - nScrollY;
        if(off >= 0){
            setSelectionFromTop(i, off);
            break;
        }
    }
}
like image 45
dslamnig Avatar answered Sep 20 '22 13:09

dslamnig


For now, ListViewCompat is probably a better solution.

android.support.v4.widget.ListViewCompat.scrollListBy(@NonNull ListView listView, int y)
like image 28
ddg Avatar answered Sep 24 '22 13:09

ddg