java 如何在 ListView 上获得滚动速度?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7895504/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get the scroll speed on a ListView?
提问by Joost
I have a ListView with onScrollStateChanged and onScroll event listeners. I want to be able to get the scroll speed of the ListView or some way to get the finalX location of the initiated scroll in some Event listener. Our app targets SDK version 7.
我有一个带有 onScrollStateChanged 和 onScroll 事件侦听器的 ListView。我希望能够获得 ListView 的滚动速度或以某种方式获得某些事件侦听器中启动滚动的 finalX 位置。我们的应用面向 SDK 版本 7。
I need to measure or get the speed at which the ListView is scrolling.
我需要测量或获取 ListView 滚动的速度。
采纳答案by SSemashko
Division first visible items difference on time difference is not a good solution. OnScroll listener recieves onScroll event every fixed period of time, so in most cases the result of division will be "0".
时差上的第一个可见项目差异不是一个很好的解决方案。OnScroll 侦听器每隔固定时间接收一次 onScroll 事件,因此大多数情况下除法的结果将为“0”。
So you can try something like this:
所以你可以尝试这样的事情:
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) {
}
};
回答by QuickNick
Try it:
试试看:
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) {
}
}
回答by rharter
So this is just some theoretical pseudo-code, but wouldn't something like this work?
所以这只是一些理论上的伪代码,但是这样的东西行不通吗?
I think the accepted answer doesn't really work the resolution is super low (i.e. you only get a velocity after an item has been scrolled off the entire screen, but what if you have large item views?).
我认为接受的答案并没有真正起作用,分辨率超低(即,只有在项目滚动离开整个屏幕后才能获得速度,但是如果项目视图很大怎么办?)。
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;
}
Again this is just pseudo-code that I haven't tested, but I think it should work. You'll also have to reset the last time and top position values when the scroll state is STATE_IDLE
.
同样,这只是我没有测试过的伪代码,但我认为它应该可以工作。当滚动状态为 时,您还必须重置上次和顶部位置值STATE_IDLE
。
回答by Autobots
There is a simple way to get the speed(NOT Velocity)of the ListView, but it's not the perfect way.
有一种简单的方法可以获取ListView的速度(不是 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;
}
- The perfect way is to use current scroll Velocitywhen callback
onScroll
,in AbsListView, we can get the velocity with FlingRunnable#mScroller.getCurrVelocity(), but unfortunately,theAbsListView
doesn't provide the public method ofgetCurrVelocity()
, so if we want to get this method, there are two methods to get it- Reflect this method, but I think it has the performance problem when get it
onScroll
callback - Copy the AbsListView.java source and create a new class of AbsListViewEx, provide a public method of
getCurrVelocity()
in this class, let new ListViewEx extends from AbsListViewEx, but it also has some problem: 1) it maybe a complex thing 2) the ListViewEx may has compatibility problem. However, this way I think is better thanReflect method
.
- Reflect this method, but I think it has the performance problem when get it
- 最完美的方法是在回调时使用当前滚动速度
onScroll
,在AbsListView中,我们可以通过FlingRunnable#mScroller.getCurrVelocity()获取速度,但遗憾的AbsListView
是,没有提供 的公共方法getCurrVelocity()
,所以如果我们想获取这个方法,有两种方法可以得到- 反映这个方法,但我认为它在get it
onScroll
callback时有性能问题 - 复制AbsListView.java源码,新建一个AbsListViewEx
getCurrVelocity()
类,在这个类中提供一个public方法,让新的ListViewEx继承自AbsListViewEx,但它也有一些问题:1)它可能是一个复杂的东西2)ListViewEx可能有兼容性问题。但是,我认为这种方式比Reflect method
.
- 反映这个方法,但我认为它在get it
回答by Dalton Tan
Here's the code if you need to know how many pixels per second it's scrolling when the items have different sizes or when you have a very long list. Calculating and caching each item size would not work if some items are missed. BTW if you choose that method, you should cache the item offset instead of the height so you don't have to do so many calculation.
如果您需要知道当项目具有不同大小或列表很长时每秒滚动多少像素,请使用以下代码。如果遗漏了某些项目,则无法计算和缓存每个项目的大小。顺便说一句,如果您选择该方法,您应该缓存项目偏移而不是高度,这样您就不必进行太多计算。
Override OnScrollListener:
覆盖 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;
}
回答by Angie
you can use android:fastScrollEnabled="true"
in you
and don't forget to : YourListView.requestFocusFromTouch();
after yourAdapter.notifyDataSetChanged();
because it (yourlistview) lose the focus in fast speed
你可以android:fastScrollEnabled="true"
在你身上使用,不要忘记:YourListView.requestFocusFromTouch();
之后yourAdapter.notifyDataSetChanged();
因为它(你的列表视图)快速失去焦点