2011-10-25 71 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; 
    }