2011-10-25 75 views
22

我有一个ListView与onScrollStateChanged和onScroll事件侦听器。我希望能够获得ListView的滚动速度,或者某种方式来获取某个Event侦听器中启动的滚动的finalX位置。我们的应用程序目标SDK版本7.如何获得ListView的滚动速度?

我需要测量或获取ListView滚动的速度。

+2

我还没有尝试过任何东西,但有两个想法浮现在脑海中。 1)使用'onFling()'方法使用手势检测器来检测手势的速度。 2)调用'listView.getFirstVisiblePosition()'来跟踪每次调用计算速度的时间。 – theisenp

+0

@theisenp看起来两个合理的选项,我会看看他们。 – Joost

+0

你可以尝试使用Scroller http://developer.android.com/reference/android/widget/Scroller.html不确定,但只是一个随机的想法。 –

回答

16

司第一个可见项目时差的差异并不是一个好的解决方案。 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) { 
    } 
}; 
+0

+1谢谢,它解决了我的问题.. –

+1

很酷,但这似乎不工作时使用快速滚动。 \t \t android:fastScrollEnabled =“true”。 – andymal

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) { 
    } 
} 
+1

你可以这样计算速度: float speed =(firstVisibleItem - lastFirstVisibleItem)/(lastTime-timeStamp)。如果您只对绝对值感兴趣,请使用Math.abs()。 – QuickNick

+0

而且你也应该乘以1 000以获得每秒元素的速度,然后设置一个阈值变得更加人性化。 – Snicolas

-1

,你可以在你 使用android:fastScrollEnabled="true",不要忘了:yourAdapter.notifyDataSetChanged();YourListView.requestFocusFromTouch();因为它(yourlistview)失去焦点在速度快

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时,您还必须重置上次和顶部位置值。

1

有一个简单的方法来得到速度(NOT速度)ListView控件的,但它不是最完美的方式。

/** 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,在AbsListView,我们可以与FlingRunnable#mScroller.getCurrVelocity()得到速度,但不幸的是,AbsListView不提供getCurrVelocity()的公共方法,所以如果我们想获得这种方法,有两种方法得到它
    • 反映这种方法,但我认为它在性能问题GET它onScroll回调
    • 复制AbsListView.java源并创建一个新类AbsListViewEx,在这个类中提供一个公共方法getCurrVelocity(),让新的ListViewEx从AbsListViewEx扩展出来,但它也有一些问题:1)它可能是一个复杂的事情2)ListViewEx可能有兼容性问题。不过,这样我觉得比Reflect method好。
  • 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; 
    }