I\'d like to get the exact, pixel position of the ListView scroll. And no, I am not referring to the first visible position.
Is there a way to achieve this?
I know I'm late to the party but I felt like sharing my solution to this problem. I have a ListView and I was trying to find how much I have scrolled in order to scroll something else relative to it and cause a parallax effect. Here's my solution:
public abstract class OnScrollPositionChangedListener implements AbsListView.OnScrollListener {
int pos;
int prevIndex;
int prevViewPos;
int prevViewHeight;
@Override
public void onScroll(AbsListView v, int i, int vi, int n) {
try {
View currView = v.getChildAt(0);
int currViewPos = Math.round(currView.getTop());
int diffViewPos = prevViewPos - currViewPos;
int currViewHeight = currView.getHeight();
pos += diffViewPos;
if (i > prevIndex) {
pos += prevViewHeight;
} else if (i < prevIndex) {
pos -= currViewHeight;
}
prevIndex = i;
prevViewPos = currViewPos;
prevViewHeight = currViewHeight;
} catch (Exception e) {
e.printStackTrace();
} finally {
onScrollPositionChanged(pos);
}
}
@Override public void onScrollStateChanged(AbsListView absListView, int i) {}
public abstract void onScrollPositionChanged(int scrollYPosition);
}
I created my own OnScrollListener where the method onScrollPositionChanged will be called every time onScroll gets called. But this method will have access to the calculated value representing the amount that the ListView has been scrolled.
To use this class, you can setOnClickListener to a new OnScrollPositionChangedListener and override the onScrollPositionChanged method.
If you need to use the onScroll method for other stuff then you can override that too but you need to call super.onScroll to get onScrollPositionChanged working correctly.
myListView.setOnScrollListener(
new OnScrollPositionChangedListener() {
@Override
public void onScroll(AbsListView v, int i, int vi, int n) {
super.onScroll(v, i, vi, n);
//Do your onScroll stuff
}
@Override
public void onScrollPositionChanged(int scrollYPosition) {
//Enjoy having access to the amount the ListView has scrolled
}
}
);
If anyone else found this in Google while looking for a way to track relative scroll offsets in an OnScrollListener - that is, change in Y since the last call to the listener - here's a Gist showing how to calculate that.
First Declare your int variable for hold the position.
int position = 0;
then add scrollListener to your ListView,
listView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
position = firstVisibleItem;
}
});
Then after getting new data or any changes in your data that time you need to set the listview current position
listView.setSelection(position);
I have used after setup my adapter , works fine for me..
Okay, I found a workaround, using the following code:
View c = listview.getChildAt(0);
int scrolly = -c.getTop() + listview.getFirstVisiblePosition() * c.getHeight();
The way it works is it takes the actual offset of the first visible list item and calculates how far it is from the top of the view to determine how much we are "scrolled into" the view, so now that we know that we can calculate the rest using the regular getFirstVisiblePosition method.
in addition to @jaredpetker answer.
ListView is not holding all the items in its scroll, so u need to operate only "visible" part of list. When you scroll down top items are shifted out and pushed as new item views. Thats how convertedView is came from (it's not empty item to fill, it's shifted item that is out of "visible" part of list. So u need to know how many items was before visible part multiply them with ListItemHeight and add headerHeight, thes how you can get real absolute offset in scroll. If u got not header, position 0 will be listItem, so you can simplify absoluteY += pos*listItemHeight;
public class CustomListView extends ListView {
private int listItemHeight = 140;
private int headerHeight = 200;
public CustomListView(Context context) {
super(context);
}
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public int computeVerticalScrollOffset() {
final int y = super.computeVerticalScrollOffset();
int absoluteY = y;
int pos = getFirstVisiblePosition();
if(pos > 0){
absoluteY += (pos-1)*listItemHeight+headerHeight;
}
//use absoluteY
return y;
}
Simplest idea I could come up with was to extend ListView and expose the "computeVerticalScrollOffset" which is protected by default, then use "com.your.package.CustomListView" in your xml layouts.
public class CustomListView extends ListView {
public CustomListView(Context context) {
super(context);
}
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public int computeVerticalScrollOffset() {
return super.computeVerticalScrollOffset();
}
}