Android ListView current scroll location Y pixels
Asked Answered
L

5

31

I'm trying to detect when a list view is scrolled beyond certain fixed threshold in pixels (half way through the first item). Unfortunately listview's getScrollY() seems to always return 0 instad of the scroll position. Is there any way to get the actual scroll location by pixel?

Here's the code I tried to use but as said it only returns 0.

getListView().setOnScrollListener(new AbsListView.OnScrollListener() {
    public void onScroll(AbsListView view, int firstVisibleItem,
                         int visibleItemCount, int totalItemCount) {
        Log.d("scroll", "scroll: " + getListView().getScrollY());
    }

    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (scrollState == 0)
        Log.d("scroll", "scrolling stopped");
    }
});
Lachrymator answered 4/10, 2012 at 12:44 Comment(1)
The correct solutionCommunity
A
54

There is no notion of Y scroll for a ListView in Android simply because the total height of the content is unknown. Only the height of the displayed content is known.

However it is possible to get the current position/Y scroll of a visible item using the following hack:

getListView().getChildAt(0).getTop();
Aragonite answered 4/10, 2012 at 13:15 Comment(1)
this doesn't work anymore. you have to add the height of all previous items of the first visible view to that piece of code. Some internal api stuff has changed recently. I hope the api is goind to include a simple function to get this value at some point. All the solutions out there are hackish and are bound to break with future API versions. One should never mess with the child views of a listviewFalsehood
G
2

A refactor code of Malachiasz's answer. This function is used for dynamic row's height.

Call onScroll listener

mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {

            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                if (mCardsListView.getChildCount() > 0) {
                    int scrollY = getScrollY();
                }
            }
        });

getScrollY function

private Dictionary<Integer, Integer> mListViewItemHeights = new Hashtable<Integer, Integer>(); 
private int getScrollY() {
        View child = mCardsListView.getChildAt(0); //this is the first visible row
        if (child == null) return 0;

        int scrollY = -child.getTop();

        mListViewItemHeights.put(mCardsListView.getFirstVisiblePosition(), child.getHeight());

        for (int i = 0; i < mCardsListView.getFirstVisiblePosition(); ++i) {
            Integer hei = mListViewItemHeights.get(i);

            //Manual add hei each row into scrollY
            if (hei != null)
                scrollY += hei;
        }

        return scrollY;
    }
Gambado answered 20/9, 2017 at 8:21 Comment(0)
D
1

You need two things to precisely define the scroll position of a listView:

To get current position:

int firstVisiblePosition = listView.getFirstVisiblePosition(); 
int topEdge=listView.getChildAt(0).getTop(); //This gives how much the top view has been scrolled.

To set the position:

listView.setSelectionFromTop(firstVisiblePosition,0);
// Note the '-' sign...  
listView.scrollTo(0,-topEdge);
Disburse answered 13/3, 2020 at 13:26 Comment(0)
H
0

You can try implementing OnTouchListener and override its onTouch(View v, MotionEvent event) and get the x and y using event.getX() and event.getY(). I had just created a demo for swipe on ListView row that will enable a delete button for deleting a particular item from the ListView. You can check the source code from my github as ListItemSwipe.

Holiness answered 4/10, 2012 at 13:4 Comment(1)
This is not an option for y-axis, since ListView scrolls depending on MotionEvent velocity.Dramatics
S
0

Maybe it will be useful for someone. Code of previous answer's will not work when the list is scrolled fast. Because in this case firstVisiblePosition may be change irregular. In my code I do not use an array to store positions

fun setOnTouchScrollListener(lv: AbsListView, onTouchScrollListener: (isDown: Boolean, offset: Int?, isScrollWorking: Boolean) -> Unit) {

    var lastItemPosition: Int = -1
    var lastItemTop: Int? = null
    var lastItemHeight: Int? = null
    var lastScrollState: Int? = AbsListView.OnScrollListener.SCROLL_STATE_IDLE

    lv.setOnScrollListener(object : AbsListView.OnScrollListener {

        override fun onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) {
            val child = lv.getChildAt(0)
            var offset: Int? = null
            var isDown: Boolean? = if (firstVisibleItem == lastItemPosition || lastItemPosition == -1) null else firstVisibleItem > lastItemPosition
            val dividerHeight = if (lv is ListView) lv.dividerHeight else 0
            val columnCount = if (lv is GridView) lv.numColumns else 1

            if (child != null) {
                if (lastItemPosition != -1) {
                    when (firstVisibleItem - lastItemPosition) {
                        0 -> {
                            isDown = if (lastItemTop == child.top) null else lastItemTop!! > child.top
                            offset = Math.abs(lastItemTop!! - child.top)
                        }
                        columnCount -> offset = lastItemHeight!! + lastItemTop!! - child.top + dividerHeight
                        -columnCount -> offset = child.height + child.top - lastItemTop!! + dividerHeight
                    }
                }

                lastItemPosition = firstVisibleItem
                lastItemHeight = child.height
                lastItemTop = child.top

                if (isDown != null && (offset == null || offset != 0)
                        && lastScrollState != AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                    onTouchScrollListener(isDown, offset, true)
                }
            }
        }

        override fun onScrollStateChanged(view: AbsListView, scrollState: Int) {
            lastScrollState = scrollState
            if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                onTouchScrollListener(true, 0, false)
                lastItemPosition = -1
                lastItemHeight = null
                lastItemTop = null
            }
        }
    })
}

Then we can use when a listview is scrolled beyond threshold

private var lastThresholdOffset = 0
private var thresholdHeight = some value

setOnTouchScrollListener(listView) { isDown: Boolean, offset: Int?, isScrollWorking: Boolean ->
    val offset = offset ?: if (isDown) 0 else thresholdHeight
    lastThresholdOffset = if (isDown) Math.min(thresholdHeight, lastThresholdOffset + offset)
    else Math.max(0, lastThresholdOffset - offset)

    val result = lastThresholdOffset > thresholdHeight
}

isScrollWorking - can be used for animation

Spatz answered 6/12, 2018 at 10:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.