2011-12-12 98 views
12

我有一個listView。當我滾動並停在某個特定的地方時。Android listView找到滾動的像素量

如何獲得我滾動的像素數量(從頂部)?

我一直在使用得到listView.getScrollY()試過了,但它返回0。

+0

這裏的[如何](http://stackoverflow.com/questions/12727594/android-listview-current-scroll-location-y-pixels/35594825#35594825 ) – Sarasranglt

回答

19

我有同樣的問題。

我不能使用View.getScrollY(),因爲它總是返回0,我不能使用OnScrollListener.onScroll(...),因爲它適用於不與像素相關的位置。我不能繼承ListView並覆蓋onScrollChanged(...),因爲它的參數值始終是0. Meh。

所有我想知道的是孩子(即listview的內容)滾動上下的數量。所以我想出了一個解決方案。我跟蹤其中一個孩子(或者你可以說其中一個「行」),並按照其垂直位置變化。

下面是代碼:

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; 
} 

} 

夫婦的注意事項:

  • 我們覆蓋onScrollChanged(...),因爲當列表視圖滾動它被調用(只是它的參數是沒有用的)
  • 然後我們從中間選擇一個孩子(行)(不一定是中間的孩子)
  • 每次滾動發生時,我們計算垂直移動根據被跟蹤的孩子的前一個位置(getTop()),我們停止跟蹤孩子,當它不安全被跟蹤時(例如,在它可能會得到重用的情況下)
+0

如果添加速度計算(例如,delta_pos/delta_time),請記住當滾動停止時視圖停止調用onScrollChanged,因此在最後一次滾動調用和下一次滾動的第一次調用之間可能會有很長時間;只是忽略(delta_time <100)或者其他一些。 – cdhabecker

+0

我用自己的方式,但最初它給我0值,你能告訴我爲什麼會這樣,我使用PullToRefresh也讓您可以指導我 –

+0

當在列表視圖初始化'onScroll'監聽器將被調用,它不使用的觸摸 – Ninja

0

嘗試實施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) { 
      } 
     }); 
+0

view.getLastVisiblePosition()..返回位置。有什麼方法可以獲得像素? – ShineDown

+0

@Jojo爲什麼你想獲得像素而不是位置? –

+2

如果listView中的項目滾動了一半,那麼我想要滾動的金額。 – ShineDown

6

你不能得到名列榜首像素(因爲那時你需要從列表頂部佈局所有視圖 - 可能有很多項目)。但你可以得到第一個可見項的像素:int pixels = listView.getChildAt(0).getTop();它通常會爲零或負數 - 顯示列表頂部和列表中第一個視圖的頂部之間的差異

+0

感謝Jin35,這使我可以找到使用第一個可見位置和兒童身高的偏移量。 –

5

編輯: 我在這個班改進,以避免某些時刻,賽道是失去因觀點過於大而不能正確得到一個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爲他的答案,這是偉大的東西,他的全部榮譽。

但後來我想現在他的回答我的進步(仍是相當多的他的答案,只是一些改進)

改進:

  • 這是一個獨立的「姿態探測器」型通過調用.setOnScrollListener()可以添加到擴展AbsListView的任何類中,因此這是一種更靈活的方法。

  • 它使用滾動狀態的變化來預先分配跟蹤的孩子,所以它不會「浪費」一個onScroll通過來分配其位置。

  • 它重新計算每onScroll通過跟蹤的孩子,以避免丟失隨機onScroll傳遞重新計算子。 (這可以通過緩存一些高度來提高效率,並且僅在一定量的滾動之後重新計算)。

希望它有助於

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); 
    } 
} 
+2

讓我納悶:爲什麼沒有安卓的開發者只需要提供與API中有這樣功能的應用程序創造者呢? 爲什麼他們不作出缺席了滾動的onScrollChanged()方法public? – JakeP

+0

我不知道。這真是糟糕的是我們有工作,但如果他們改變現在,他們將在5年有點晚了。也許在支持庫中發佈一些東西。無論如何,爲了讓你知道我用更好的解決方案編輯了答案。 – Budius

+0

這是一個偉大的一段代碼,但是我想問一下,是可以檢測的反彈時使用我們的代碼的ListView量? –