У меня есть listView. Когда я прокручиваю и останавливаюсь в определенном месте.
Как я могу получить количество пикселей, которые я прокрутил (сверху)?
Я попытался использовать get listView.getScrollY(), но он возвращает 0.
У меня есть listView. Когда я прокручиваю и останавливаюсь в определенном месте.
Как я могу получить количество пикселей, которые я прокрутил (сверху)?
Я попытался использовать get listView.getScrollY(), но он возвращает 0.
У меня была та же проблема.
Я не могу использовать View.getScrollY(), потому что он всегда возвращает 0, и я не могу использовать OnScrollListener.onScroll(...), потому что он работает с позициями не с пикселями. Я не могу подклассировать ListView и переопределить onScrollChanged (...), потому что его значения параметров всегда равны 0. Meh.
Все, что я хочу знать, это количество, за которое прокручиваются дети (т.е. содержимое списка) вверх или вниз. Поэтому я придумал решение. Я отслеживаю одного из детей (или вы можете сказать один из "строк" ) и следуйте его изменению по вертикали.
Вот код:
public class ObservableListView extends ListView {
public static interface ListViewObserver {
public void onScroll(float deltaY);
}
private ListViewObserver mObserver;
private View mTrackedChild;
private int mTrackedChildPrevPosition;
private int mTrackedChildPrevTop;
public ObservableListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mTrackedChild == null) {
if (getChildCount() > 0) {
mTrackedChild = getChildInTheMiddle();
mTrackedChildPrevTop = mTrackedChild.getTop();
mTrackedChildPrevPosition = getPositionForView(mTrackedChild);
}
} else {
boolean childIsSafeToTrack = mTrackedChild.getParent() == this && getPositionForView(mTrackedChild) == mTrackedChildPrevPosition;
if (childIsSafeToTrack) {
int top = mTrackedChild.getTop();
if (mObserver != null) {
float deltaY = top - mTrackedChildPrevTop;
mObserver.onScroll(deltaY);
}
mTrackedChildPrevTop = top;
} else {
mTrackedChild = null;
}
}
}
private View getChildInTheMiddle() {
return getChildAt(getChildCount() / 2);
}
public void setObserver(ListViewObserver observer) {
mObserver = observer;
}
}
Пара примечаний:
Вы не можете получить пиксели из верхней части списка (потому что тогда вам нужно отобразить все виды сверху списка - может быть много элементов). Но вы можете получить пиксели первого видимого элемента: int pixels = listView.getChildAt(0).getTop();
он обычно будет нулевым или отрицательным числом - показывает разницу между вершиной listView и верхней частью первого представления в списке
изменить
Я улучшил этот класс, чтобы избежать некоторых моментов, которые проигрывал трек из-за слишком больших представлений и не получения getTop()
Это новое решение использует 4 точки отслеживания:
что мы всегда имеем isSafeToTrack
, равный true
import android.view.View;
import android.widget.AbsListView;
/**
* Created by budius on 16.05.14.
* This improves on Zsolt Safrany answer on stack-overflow (see link)
* by making it a detector that can be attached to any AbsListView.
* http://stackoverflow.com/questions/8471075/android-listview-find-the-amount-of-pixels-scrolled
*/
public class PixelScrollDetector implements AbsListView.OnScrollListener {
private final PixelScrollListener listener;
private TrackElement[] trackElements = {
new TrackElement(0), // top view, bottom Y
new TrackElement(1), // mid view, bottom Y
new TrackElement(2), // mid view, top Y
new TrackElement(3)};// bottom view, top Y
public PixelScrollDetector(PixelScrollListener listener) {
this.listener = listener;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// init the values every time the list is moving
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL ||
scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
for (TrackElement t : trackElements)
t.syncState(view);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
boolean wasTracked = false;
for (TrackElement t : trackElements) {
if (!wasTracked) {
if (t.isSafeToTrack(view)) {
wasTracked = true;
if (listener != null)
listener.onScroll(view, t.getDeltaY());
t.syncState(view);
} else {
t.reset();
}
} else {
t.syncState(view);
}
}
}
public static interface PixelScrollListener {
public void onScroll(AbsListView view, float deltaY);
}
private static class TrackElement {
private final int position;
private TrackElement(int position) {
this.position = position;
}
void syncState(AbsListView view) {
if (view.getChildCount() > 0) {
trackedChild = getChild(view);
trackedChildPrevTop = getY();
trackedChildPrevPosition = view.getPositionForView(trackedChild);
}
}
void reset() {
trackedChild = null;
}
boolean isSafeToTrack(AbsListView view) {
return (trackedChild != null) &&
(trackedChild.getParent() == view) && (view.getPositionForView(trackedChild) == trackedChildPrevPosition);
}
int getDeltaY() {
return getY() - trackedChildPrevTop;
}
private View getChild(AbsListView view) {
switch (position) {
case 0:
return view.getChildAt(0);
case 1:
case 2:
return view.getChildAt(view.getChildCount() / 2);
case 3:
return view.getChildAt(view.getChildCount() - 1);
default:
return null;
}
}
private int getY() {
if (position <= 1) {
return trackedChild.getBottom();
} else {
return trackedChild.getTop();
}
}
View trackedChild;
int trackedChildPrevPosition;
int trackedChildPrevTop;
}
}
оригинальный ответ:
Прежде всего, я хочу поблагодарить @zsolt-safrany за его ответ, это был отличный материал, полное признание для него.
Но потом я хочу представить свое улучшение в его ответе (все еще в значительной степени его ответ, всего несколько улучшений)
Улучшения:
Это отдельный тип класса жестов, который может быть добавлен к любому классу, который расширяет AbsListView
, вызывая .setOnScrollListener()
, поэтому это более гибкий подход.
Он использует изменение состояния прокрутки для предварительного выделения отслеживаемого дочернего элемента, поэтому он не "тратит" один onScroll
pass, чтобы выделить его позицию.
Он пересчитывает отслеживаемый ребенок на каждом проходе onScroll
, чтобы избежать пропущенного случайного прохода SSProll для пересчета дочернего элемента. (это может быть более эффективным путем кеширования некоторых высот и только пересчета после определенного количества прокрутки).
надеюсь, что это поможет
import android.view.View;
import android.widget.AbsListView;
/**
* Created by budius on 16.05.14.
* This improves on Zsolt Safrany answer on stack-overflow (see link)
* by making it a detector that can be attached to any AbsListView.
* http://stackoverflow.com/questions/8471075/android-listview-find-the-amount-of-pixels-scrolled
*/
public class PixelScrollDetector implements AbsListView.OnScrollListener {
private final PixelScrollListener listener;
private View mTrackedChild;
private int mTrackedChildPrevPosition;
private int mTrackedChildPrevTop;
public PixelScrollDetector(PixelScrollListener listener) {
this.listener = listener;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// init the values every time the list is moving
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL ||
scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
if (mTrackedChild == null) {
syncState(view);
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mTrackedChild == null) {
// case we don't have any reference yet, try again here
syncState(view);
} else {
boolean childIsSafeToTrack = (mTrackedChild.getParent() == view) && (view.getPositionForView(mTrackedChild) == mTrackedChildPrevPosition);
if (childIsSafeToTrack) {
int top = mTrackedChild.getTop();
if (listener != null) {
float deltaY = top - mTrackedChildPrevTop;
listener.onScroll(view, deltaY);
}
// re-syncing the state make the tracked child change as the list scrolls,
// and that gives a much higher true state for `childIsSafeToTrack`
syncState(view);
} else {
mTrackedChild = null;
}
}
}
private void syncState(AbsListView view) {
if (view.getChildCount() > 0) {
mTrackedChild = getChildInTheMiddle(view);
mTrackedChildPrevTop = mTrackedChild.getTop();
mTrackedChildPrevPosition = view.getPositionForView(mTrackedChild);
}
}
private View getChildInTheMiddle(AbsListView view) {
return view.getChildAt(view.getChildCount() / 2);
}
public static interface PixelScrollListener {
public void onScroll(AbsListView view, float deltaY);
}
}
Попробуйте реализовать OnScrollListener:
list.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
int last = view.getLastVisiblePosition();
break;
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
}
});