2012-05-30 95 views
35

我想要獲取ListView滾動條的確切像素位置。 不,我不是指第一個可見的位置。Android獲取ListView中滾動位置的確切位置

有沒有辦法做到這一點?

+0

您所提供的方法解決不同的問題 - 如何恢復的ListView的位置,而我的問題是如何獲取滾動位置的實際值。 – saarraz1

回答

71

好吧,我找到了一個解決方法,使用下面的代碼:

View c = listview.getChildAt(0); 
int scrolly = -c.getTop() + listview.getFirstVisiblePosition() * c.getHeight(); 

它的工作方式是需要實際的第一個可見的列表項的偏移和計算是多麼遠的頂部我們知道我們可以使用常規的getFirstVisiblePosition方法來計算其餘部分,從而確定我們「滾動」到視圖中的程度。

+10

這裏最好的答案http://stackoverflow.com/a/5688490/1087300 – Chet

+88

只有當你的所有列表項目都是相同的高度時纔是準確的 –

+6

如果ListView有一個頭部? – Koruk

29

Saarraz1的回答只有在列表視圖中的所有行高度相同且沒有標題(或高度與行相同)的情況下才有效。

請注意,一旦行消失在屏幕的頂部,您無法訪問它們,因爲您將無法跟蹤其高度。這就是爲什麼你需要保存這些高度(或所有高度的累積)。我的解決方案需要爲每個索引保留一個高度字典(假設當列表第一次被滾動到頂部時顯示)。

private Dictionary<Integer, Integer> listViewItemHeights = new Hashtable<Integer, Integer>(); 

private int getScroll() { 
    View c = listView.getChildAt(0); //this is the first visible row 
    int scrollY = -c.getTop(); 
    listViewItemHeights.put(listView.getFirstVisiblePosition(), c.getHeight()); 
    for (int i = 0; i < listView.getFirstVisiblePosition(); ++i) { 
     if (listViewItemHeights.get(i) != null) // (this is a sanity check) 
      scrollY += listViewItemHeights.get(i); //add all heights of the views that are gone 
    } 
    return scrollY; 
} 
+0

我很困惑這個解決方案。你什麼時候調用這個方法?你只在你的字典中存儲單個值? –

+0

不,不是一個。當您向下滾動列表時,listView.getFirstVisiblePosition()的值會發生變化。一旦頂行消失在頂部,我無法檢查它們的高度。 – Maria

+1

當然,這隻有在用戶手動滾動瀏覽所有列表項時纔有效。如果您以編程方式設置列表位置,則這不起作用。 –

9

如果任何人發現這在谷歌,而尋找一種方法來跟蹤在OnScrollListener 相對滾動偏移 - 即,因爲到監聽器的最後一次通話Y中的變化 - here's a Gist showing how to calculate that

+0

我幾乎試圖計算listview的positiom。我希望它有效,我會明天再試。 –

+1

[Here's](http://stackoverflow.com/questions/12727594/android-listview-current-scroll-location-y-pixels/35594825#35594825)如何使用它 – Sarasranglt

20

我想到的最簡單的想法是擴展ListView並公開默認情況下受保護的「computeVerticalScrollOffset」,然後在您的xml佈局中使用「com.your.package.CustomListView」。

public class CustomListView extends ListView { 

    public CustomListView(Context context) { 
     super(context); 
    } 

    public CustomListView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public CustomListView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    @Override 
    public int computeVerticalScrollOffset() { 
     return super.computeVerticalScrollOffset(); 
    } 
} 
+0

這是一個不錯的竅門,但不幸的是結果在一些奇怪的單位,例如雖然頭部部分可見,但computeVerticalScrollOffset()報告展開此頭部視圖的百分比。當我滾動更多時,結果單調增長,但我不確定如何從第一個標題的(虛擬)頂部以像素爲單位轉換。 –

+0

computeVerticalScrollOffset與「可見」列表中的第一項有關。您需要了解ListView計算它的方式。你是否記得Adapter中的getView(int position,View convertView,ViewGroup parent)方法?從列表頂部的舊項目被淘汰並轉換成底部的項目,反之亦然。你可以使用getFirstVisiblePosition()。 ''' y = super.computeVerticalScrollOffset(); (listView.getFirstVisiblePosition()> 0)y + =(listView.getFirstVisiblePosition() - 1)* listItemHeight + headerHeight; } ''' –

+0

增加完整的例子作爲新的答案 –

5

首先聲明你的int變量來保存位置。

int position = 0; 

然後加入scrollListener到你的ListView,

listView.setOnScrollListener(new OnScrollListener() { 

     @Override 
     public void onScrollStateChanged(AbsListView view, int scrollState) { 
      // TODO Auto-generated method stub 

     } 

     @Override 
     public void onScroll(AbsListView view, int firstVisibleItem, 
       int visibleItemCount, int totalItemCount) { 
      position = firstVisibleItem; 

     } 
    }); 

然後獲得新的數據或任何變化,你的數據在該時間之後,你需要設置列表視圖當前位置

listView.setSelection(position); 

我已經使用安裝我的適配器後,對我來說工作正常..

+1

這不回答OP的問題,但它是一個簡單的解決方案,設置列表視圖的滾動位置基於其以前的位置,獨立的單元格大小。 – Grux

2

我知道我遲到了但我覺得分享我的解決方案來解決這個問題。我有一個ListView,我試圖找到我滾動了多少,以便相對於它滾動其他東西並導致視差效果。這裏是我的解決方案:

public abstract class OnScrollPositionChangedListener implements AbsListView.OnScrollListener { 
    int pos; 
    int prevIndex; 
    int prevViewPos; 
    int prevViewHeight; 
    @Override 
    public void onScroll(AbsListView v, int i, int vi, int n) { 
     try { 
      View currView = v.getChildAt(0); 
      int currViewPos = Math.round(currView.getTop()); 
      int diffViewPos = prevViewPos - currViewPos; 
      int currViewHeight = currView.getHeight(); 
      pos += diffViewPos; 
      if (i > prevIndex) { 
       pos += prevViewHeight; 
      } else if (i < prevIndex) { 
       pos -= currViewHeight; 
      } 
      prevIndex = i; 
      prevViewPos = currViewPos; 
      prevViewHeight = currViewHeight; 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } finally { 
      onScrollPositionChanged(pos); 
     } 
    } 
    @Override public void onScrollStateChanged(AbsListView absListView, int i) {} 
    public abstract void onScrollPositionChanged(int scrollYPosition); 
} 

我創建了自己OnScrollListener其中onScrollPositionChanged都會被調用onScroll被稱爲時間的方法。但是,這種方法將有權訪問表示ListView滾動量的計算值。

要使用此類,可以將setOnClickListener設置爲新的OnScrollPositionChangedListener,並覆蓋onScrollPositionChanged方法。

如果你需要爲其他東西使用onScroll方法,那麼你也可以重寫,但你需要調用super.onScroll來讓onScrollPositionChanged正常工作。

myListView.setOnScrollListener(
    new OnScrollPositionChangedListener() { 
     @Override 
     public void onScroll(AbsListView v, int i, int vi, int n) { 
      super.onScroll(v, i, vi, n); 
      //Do your onScroll stuff 
     } 
     @Override 
     public void onScrollPositionChanged(int scrollYPosition) { 
      //Enjoy having access to the amount the ListView has scrolled 
     } 
    } 
); 
+0

謝謝!我有同樣的想法,我沒有複製你的樣本。但對我來說,這並不能正確工作,當我努力測試速度時,我已經抵消了。 – fingerup

1

我曾面臨類似的問題,那我想放置垂直搜索欄的ListView當前滾動值。所以我有我自己的解決方案。

首先創建

public abstract class OnScrollPositionChangedListener implements AbsListView.OnScrollListener { 
    int pos; 
    public int viewHeight = 0; 
    public int listHeight = 0; 

    @Override 
    public void onScroll(AbsListView v, int i, int vi, int n) { 
     try { 
      if(viewHeight==0) { 
       viewHeight = v.getChildAt(0).getHeight(); 
       listHeight = v.getHeight(); 

      } 
      pos = viewHeight * i; 

     } catch (Exception e) { 
      e.printStackTrace(); 
     } finally { 
      onScrollPositionChanged(pos); 
     } 
    } 
    @Override public void onScrollStateChanged(AbsListView absListView, int i) {} 
    public abstract void onScrollPositionChanged(int scrollYPosition); 
} 

然後用它主要活動這樣。

public class MainActivity extends AppCompatActivity { 
    SeekBar seekBar; 
    ListView listView; 
    OnScrollPositionChangedListener onScrollPositionChangedListener = new OnScrollPositionChangedListener() { 
     @Override 
     public void onScroll(AbsListView v, int i, int vi, int n) { 
      super.onScroll(v, i, vi, n); 
      //Do your onScroll stuff 
     } 

     @Override 
     public void onScrollPositionChanged(int scrollYPosition) { 
      //Enjoy having access to the amount the ListView has scrolled 

      seekBar.setProgress(scrollYPosition); 


     } 
    }; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     seekBar = (SeekBar) findViewById(R.id.mySeekBar); 
     listView = (ListView) findViewById(R.id.listView); 
     final String[] values = new String[]{"Android List View", 
       "Adapter implementation", 
       "Simple List View In Android", 
       "Create List View Android", 
       "Android Example", 

     }; 

     ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, 
       android.R.layout.simple_list_item_1, android.R.id.text1, values); 
     listView.setAdapter(adapter); 
     listView.setOnScrollListener(onScrollPositionChangedListener); 



     seekBar.postDelayed(new Runnable() { 
      @Override 
      public void run() { 
       seekBar.setMax((onScrollPositionChangedListener.viewHeight * values.length) - onScrollPositionChangedListener.listHeight); 
      } 
     }, 1000); 
     seekBar.setEnabled(false); 

    } 
} 

在應用搖籃

compile 'com.h6ah4i.android.widget.verticalseekbar:verticalseekbar:0.7.0' 

在XML佈局

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    android:id="@+id/activity_main" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:paddingBottom="@dimen/activity_vertical_margin" 
    android:paddingLeft="@dimen/activity_horizontal_margin" 

    android:paddingRight="@dimen/activity_horizontal_margin" 
    android:paddingTop="@dimen/activity_vertical_margin" 
    tools:context="com.centsol.charexamples.MainActivity"> 


    <ListView 
     android:id="@+id/listView" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:fadeScrollbars="false" 


     android:scrollbars="none" 

     android:layout_alignParentTop="true" 

     android:layout_alignParentLeft="true" 
     android:layout_alignParentStart="true"> 

    </ListView> 
    <!-- This library requires pair of the VerticalSeekBar and VerticalSeekBarWrapper classes --> 
    <com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper 
     android:layout_width="wrap_content" 

     android:layout_alignParentRight="true" 
     android:layout_height="match_parent"> 
     <com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBar 
      android:id="@+id/mySeekBar" 
      android:layout_width="0dp" 
      android:progressDrawable="@drawable/progress" 
      android:thumb="@drawable/thumb" 
      android:layout_height="0dp" 
      android:splitTrack="false" 

      app:seekBarRotation="CW90" /> <!-- Rotation: CW90 or CW270 --> 
    </com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper> 
    <View 
     android:layout_width="1dp" 
     android:visibility="visible" 
     android:layout_alignParentRight="true" 
     android:layout_marginTop="16dp" 
     android:layout_marginRight="2.5dp" 
     android:layout_marginBottom="16dp" 
     android:background="@android:color/black" 
     android:layout_height="match_parent" /> 
</RelativeLayout> 

背景XML

<?xml version="1.0" encoding="utf-8"?> 
<shape xmlns:android="http://schemas.android.com/apk/res/android" > 

    <solid android:color="@android:color/transparent"/> 

</shape> 

填充XML

<?xml version="1.0" encoding="UTF-8"?> 
<shape xmlns:android="http://schemas.android.com/apk/res/android" > 

<solid android:color="@android:color/transparent" /> 


</shape> 

進步XML

<?xml version="1.0" encoding="utf-8"?> 
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > 

    <item 
     android:id="@android:id/background" 
     android:drawable="@drawable/background"/> 
    <item android:id="@android:id/progress"> 
     <clip android:drawable="@drawable/fill" /> 
    </item> 

</layer-list> 

拇指XML

<?xml version="1.0" encoding="UTF-8"?> 
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
    android:shape="oval" > 


<solid android:color="@android:color/holo_red_dark" /> 
    <size 
     android:height="5dp" 
     android:width="5dp" /> 

</shape> 
1
除了 @jaredpetker答案

。 ListView沒有在其滾動中保存所有的項目,所以你只需要操作列表中的「可見」部分。當您向下滾動時,頂部項目將被移出並推送爲新的項目視圖。這就是轉換視圖的來源(它不是空的項目來填充,它的項目已移出列表中的「可見」部分。所以你需要知道在可見部分乘以ListItemHeight並添加headerHeight之前有多少項目,這是如何你可以得到真正的絕對的滾動偏移量。如果你長了沒有頭,位置0會的listItem,這樣可以簡化absoluteY += pos*listItemHeight;

public class CustomListView extends ListView { 

private int listItemHeight = 140; 
private int headerHeight = 200; 

public CustomListView(Context context) { 
    super(context); 
} 

public CustomListView(Context context, AttributeSet attrs) { 
    super(context, attrs); 
} 

public CustomListView(Context context, AttributeSet attrs, int defStyle) { 
    super(context, attrs, defStyle); 
} 

@Override 
public int computeVerticalScrollOffset() { 
    final int y = super.computeVerticalScrollOffset(); 
    int absoluteY = y; 
    int pos = getFirstVisiblePosition(); 
    if(pos > 0){ 
     absoluteY += (pos-1)*listItemHeight+header‌​Height; 
    } 
    //use absoluteY 
    return y; 
}