2012-05-30 3 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

Если кто-то нашел в Google, ища способ отслеживания относительно смещения прокрутки в OnScrollListener - то есть, изменение Y с момента последнего вызова к слушателю - here's a Gist showing how to calculate that.

+0

Я почти попытаюсь вычислить, что positeom listview. Надеюсь, это сработает, я постараюсь завтра. –

+1

[Здесь] (http://stackoverflow.com/questions/12727594/android-listview-current-scroll-location-y-pixels/35594825#35594825) как его использовать – Sarasranglt

20

Простейшей идеей, которую я мог придумать, было расширить ListView и выставить «computeVerticalScrollOffset», который по умолчанию защищен, а затем использовать «com.your.package.CustomListView» в ваших xml-макетах.

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 вычисляет его. Вы помните getView (int position, View convertView, ViewGroup parent) в Adapter? старый элемент сверху списка выставляется и преобразуется в элемент внизу и наоборот. Вы можете использовать getFirstVisiblePosition(). '' ' y = super.computeVerticalScrollOffset(); if (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

listView.setSelection(position); 

I использовали после настройки мой адаптер, отлично работает для меня ..

+1

Это не отвечает на вопрос ОП, но это простое решение для установки положения прокрутки списка в зависимости от его предыдущей позиции, независимо от размера ячейки. – 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.

Чтобы использовать этот класс, вы можете установитьOnClickListener в новый 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

я столкнулся с подобной проблемой, что я хотел поместить Vertical SeekBar по текущим прокручивать значения 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); 

    } 
} 

в App Gradle

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 не удерживает все элементы в своем прокрутке, поэтому вам нужно использовать только «видимую» часть списка. Когда вы прокручиваете вниз, верхние элементы сдвигаются и отображаются как новые элементы. То, как происходит преобразованиеView (это не пустой элемент для заполнения, это сдвинутый элемент, который не входит в «видимую» часть списка. Поэтому вам нужно знать, сколько элементов было перед видимой частью, умножить их на ListItemHeight и добавить headerHeight, thes how вы можете получить реальное абсолютное смещение в свитке Если и получил не заголовок, позиция 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; 
} 
Смежные вопросы