我有一个带有onScrollStateChanged和onScroll事件监听器的ListView。我希望能够获得ListView的滚动速度或某种方式来获取某些事件监听器中启动的滚动的finalX位置。我们的应用程序针对SDK版本7。
我需要测量或获得ListView滚动的速度。
答案 0 :(得分:17)
分区第一个可见项目的时差差异不是一个好的解决方案。 OnScroll侦听器在每个固定的时间段内接收onScroll事件,因此在大多数情况下,除法的结果将为“0”。
所以你可以尝试这样的事情:
private OnScrollListener onScrollListener = new OnScrollListener() {
private int previousFirstVisibleItem = 0;
private long previousEventTime = 0;
private double speed = 0;
@Override
public void onScroll(HtcAbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (previousFirstVisibleItem != firstVisibleItem){
long currTime = System.currentTimeMillis();
long timeToScrollOneElement = currTime - previousEventTime;
speed = ((double)1/timeToScrollOneElement)*1000;
previousFirstVisibleItem = firstVisibleItem;
previousEventTime = currTime;
Log.d("DBG", "Speed: " +speed + " elements/second");
}
}
@Override
public void onScrollStateChanged(HtcAbsListView view, int scrollState) {
}
};
答案 1 :(得分:5)
试一试:
private class SpeedMeterOnScrollListener implements OnScrollListener {
private long timeStamp;
private int lastFirstVisibleItem;
public SpeedMeterOnScrollListener() {
timeStamp = System.currentTimeMillis();
lastFirstVisibleItem = 0;
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
long lastTime = System.currentTimeMillis();
//calculate speed by firstVisibleItem, lastFirstVisibleItem, timeStamp and lastTime
timeStamp = lastTime;
lastFirstVisibleItem = firstVisibleItem;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
}
答案 2 :(得分:2)
所以这只是一些理论上的伪代码,但不会像这样工作吗?
我认为接受的答案并不真正起作用,分辨率是超低的(即只有在项目滚动到整个屏幕后才能获得速度,但如果你有大项目视图怎么办?)。
int mTrackingPosition = -1;
int mLastTop;
long mLastEventTime;
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// Get a new tracking position if our old one is no longer on screen
// (this also includes the first time)
if (first > mTrackingPosition || last < mTrackingPosition) {
// We get the middle position here since that's likely to stay
// on screen for a bit when scrolling up or down.
mTrackingPosition = firstVisibleItem + visibleItemCount / 2;
// Reset our times since we can't really get velocity from this
// one measurement
mLastTop = mLastEventTime = -1;
// Handle the case that this happens more than once in a
// row if that's even reasonably possible (i.e. they
// scrolled rediculously fast)
}
// Get the measurements of the tracking view
View v = view.getViewForPosition(mTrackingPosition);
int top = v.getTop();
long time = System.currentTimeMillis();
// We can only get speed if we have a last recorded time
if (mLastTop != -1 && mLastEventTime != -1) {
// Velocity = distance / time
float velocity = (mLastTop - top) / (time - mLastEventTime);
// What do you want to do with the velocity?
...
}
// Update the last top and time so that we can track the difference
mLastTop = top;
mLastEventTime = time;
}
这只是我没有测试的伪代码,但我认为它应该可行。当滚动状态为STATE_IDLE
时,您还必须重置上次时间和最高位置值。
答案 3 :(得分:1)
有一种简单的方法可以获得ListView的速度 (NOT Velocity) ,但它并不完美方式。
/** The scroll speed threshold, it's a empiric value. */
private static final int LOW_SPEED_SCROLL_THRESHOLD = 3000;
/** The offset, in pixels, by which the content of this view is scrolled vertically. */
private long mScrollY = 0;
/** The last offset, in pixels, by which the content of this view is scrolled vertically. */
private long mLastScrollY = 0;
/** The last scroll time, in millisecond */
private long mLastTime = 0;
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
final View currentItemView = absListView.getChildAt(0);
if (currentItemView != null) {
// The first try to scroll, reset the last time flag
if (mLastTime == 0) {
mLastTime = System.currentTimeMillis();
return;
}
final int height = currentItemView.getHeight();
final int currentPos = firstVisibleItem;
final int currentPosOffset = currentItemView.getTop(); // < 0
mScrollY = currentPos * height - currentPosOffset;
final long deltaOffset = mScrollY - mLastScrollY;
final long currTime = System.currentTimeMillis();
if (currTime == mLastTime) {
return;
}
final long speed = deltaOffset * 1000 / (currTime - mLastTime);
if (Math.abs(speed) < LOW_SPEED_SCROLL_THRESHOLD) {
// low speed
} else {
// high speed
}
mLastTime = currTime;
mLastScrollY = mScrollY;
} else {
resetScrollState();
}
}
private void resetScrollState() {
mLastTime = 0;
mScrollY = 0;
mLastScrollY = 0;
}
onScroll
时使用当前滚动 Velocity ,在AbsListView中,我们可以使用 FlingRunnable#mScroller.getCurrVelocity()获取速度但不幸的是,AbsListView
并没有提供getCurrVelocity()
的公共方法,所以如果我们想要获得此方法,有两种方法可以获得它
onScroll
回调时存在性能问题getCurrVelocity()
的公共方法,让新的ListViewEx从AbsListViewEx扩展,但它也有一些问题:1)它可能是一个复杂的事情2)ListViewEx可能有兼容性问题。但是,我认为这种方式比Reflect method
更好。答案 4 :(得分:0)
如果您需要知道当项目具有不同的大小或者您有一个很长的列表时,它需要知道它每秒滚动多少像素,这是代码。如果遗漏了某些项目,则计算和缓存每个项目大小将不起作用。顺便说一句,如果你选择那种方法,你应该缓存项目偏移而不是高度,这样你就不必进行这么多的计算。
覆盖OnScrollListener:
private HashMap<Integer, Integer> offsetMap = new HashMap<>();
private long prevScrollTime = System.currentTimeMillis();
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
float traveled = 0;
Set<Integer> oldKeys = new HashSet<>(offsetMap.keySet());
for (int i = 0; i < visibleItemCount; i++) {
int pos = firstVisibleItem + i;
int newOffset = view.getChildAt(i).getTop();
if (offsetMap.containsKey(pos) && traveled == 0) {
traveled = Math.abs(newOffset - offsetMap.get(pos));
}
offsetMap.put(pos, newOffset);
oldKeys.remove(pos);
}
// remove those that are no longer in view
for (Integer key : oldKeys) {
offsetMap.remove(key);
}
long newTime = System.currentTimeMillis();
long t = newTime - prevScrollTime;
if (t > 0) {
float speed = traveled / t * 1000f;
} else {
// speed = 0
}
prevScrollTime = newTime;
}
答案 5 :(得分:-1)
您可以使用android:fastScrollEnabled="true"
并且不要忘记YourListView.requestFocusFromTouch();
之后yourAdapter.notifyDataSetChanged();
,因为它(yourlistview)以快速的速度失去焦点