Подтвердить что ты не робот

Android получает точную позицию прокрутки в ListView

Я хочу получить точную позицию пикселя прокрутки ListView. И нет, я не имею в виду первую видимую позицию.

Есть ли способ достичь этого?

4b9b3361

Ответ 1

Хорошо, я нашел обходное решение, используя следующий код:

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

Как он работает, он принимает фактическое смещение первого элемента видимого списка и вычисляет, насколько далеко он находится от вершины представления, чтобы определить, сколько мы "прокручиваем" в представление, поэтому теперь, когда мы знаем, что мы можем рассчитать остаток, используя обычный метод getFirstVisiblePosition.

Ответ 2

Ответ 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;
}

Ответ 3

Простейшей идеей, которую я мог придумать, было расширить 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();
    }
}

Ответ 4

Если кто-то еще нашел это в Google, ища способ отслеживать относительные смещения прокрутки в OnScrollListener, то есть изменить Y с момента последнего вызова слушателя - здесь Gist показывает, как рассчитать это.

Ответ 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);

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

Ответ 6

Я знаю, что опаздываю на вечеринку, но мне хотелось поделиться своим решением этой проблемы. У меня есть 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 будет вызываться каждый раз, когда вызывается вызов Scroll. Но этот метод будет иметь доступ к вычисленному значению, представляющему сумму, которую прокрутила 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
        }
    }
);

Ответ 7

Я столкнулся с аналогичной проблемой: мне захотелось разместить Вертикальный поиск при текущем прокрученном значении 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);

    }
}

в приложении 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>

background 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>

thumb 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>

Ответ 8

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