Android 在 ListView 中获取精确的滚动位置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10808387/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 05:02:43  来源:igfitidea点击:

Android getting exact scroll position in ListView

androidandroid-listview

提问by saarraz1

I'd like to get the exact, pixel position of the ListView scroll. And no, I am not referring to the first visible position.

我想获得 ListView 滚动的准确像素位置。不,我不是指第一个可见位置。

Is there a way to achieve this?

有没有办法实现这一目标?

回答by saarraz1

Okay, I found a workaround, using the following code:

好的,我找到了一种解决方法,使用以下代码:

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

The way it works is it takes the actual offset of the first visible list item and calculates how far it is from the top of the view to determine how much we are "scrolled into" the view, so now that we know that we can calculate the rest using the regular getFirstVisiblePosition method.

它的工作方式是获取第一个可见列表项的实际偏移量并计算它离视图顶部的距离,以确定我们“滚动到”视图中的距离,所以现在我们知道我们可以计算了其余的使用常规的 getFirstVisiblePosition 方法。

回答by Maria

Saarraz1's answer will only work if all the rows in the listview are of the same height and there's no header (or it is also the same height as the rows).

Saarraz1 的答案只有在列表视图中的所有行都具有相同的高度并且没有标题(或者它也与行的高度相同)时才有效。

Note that once the rows disappear at the top of the screen you don't have access to them, as in you won't be able to keep track of their height. This is why you need to save those heights (or accumulated heights of all). My solution requires keeping a Dictionary of heights per index (it is assumed that when the list is displayed the first time it is scrolled to the top).

请注意,一旦行在屏幕顶部消失,您将无法访问它们,因为您将无法跟踪它们的高度。这就是为什么您需要保存这些高度(或所有高度的累积高度)。我的解决方案需要保留每个索引的高度字典(假设第一次显示列表时将其滚动到顶部)。

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

回答by jaredpetker

Simplest idea I could come up with was to extend ListView and expose the "computeVerticalScrollOffset" which is protected by default, then use "com.your.package.CustomListView" in your xml layouts.

我能想到的最简单的想法是扩展 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();
    }
}

回答by cnnr

If anyone else found this in Google while looking for a way to track relativescroll offsets in an OnScrollListener - that is, change in Y since the last call to the listener - here's a Gist showing how to calculate that.

如果其他人在 Google 中寻找跟踪OnScrollListener 中的相对滚动偏移量的方法时发现了这一点- 即自上次调用侦听器以来 Y 的变化 -这里有一个 Gist 显示如何计算它

回答by CrazyMind

First Declare your int variable for hold the position.

首先声明您的 int 变量以保持位置。

int position = 0;

then add scrollListener to your ListView,

然后将 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;

        }
    });

Then after getting new data or any changes in your data that time you need to set the listview current position

然后在获取新数据或数据中的任何更改之后,您需要设置列表视图当前位置

listView.setSelection(position);

I have used after setup my adapter , works fine for me..

我在设置适配器后使用过,对我来说很好用..

回答by aptaunk

I know I'm late to the party but I felt like sharing my solution to this problem. I have a ListView and I was trying to find how much I have scrolled in order to scroll something else relative to it and cause a parallax effect. Here's my solution:

我知道我迟到了,但我想分享我对这个问题的解决方案。我有一个 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);
}

I created my own OnScrollListener where the method onScrollPositionChanged will be called every time onScroll gets called. But this method will have access to the calculated value representing the amount that the ListView has been scrolled.

我创建了自己的 OnScrollListener ,每次调用 onScroll 时都会调用方法 onScrollPositionChanged 。但是此方法将可以访问表示 ListView 已滚动量的计算值。

To use this class, you can setOnClickListener to a new OnScrollPositionChangedListener and override the onScrollPositionChanged method.

要使用此类,您可以将 OnClickListener 设置为新的 OnScrollPositionChangedListener 并覆盖 onScrollPositionChanged 方法。

If you need to use the onScroll method for other stuff then you can override that too but you need to call super.onScroll to get onScrollPositionChanged working correctly.

如果您需要将 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
        }
    }
);

回答by Ali Imran

I had faced the similar problem, That I wanted to place the Vertical Seekbarat current scrolled value of ListView. So I have my own solution like this.

我遇到了类似的问题,我想将垂直搜索栏放置ListView 的当前滚动值处。所以我有我自己的解决方案。

First Create Class

首先创建类

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

Then use it in Main Activitylike this.

然后像这样在主活动中使用它。

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

    }
}

in App Gradle

在 App Gradle 中

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

In XML Layout

在 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

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

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

</shape>

fill xml

填写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>

progress xml

进度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

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

回答by El'

in addition to @jaredpetkeranswer. ListView is not holding all the items in its scroll, so u need to operate only "visible" part of list. When you scroll down top items are shifted out and pushed as new item views. Thats how convertedView is came from (it's not empty item to fill, it's shifted item that is out of "visible" part of list. So u need to know how many items was before visible part multiply them with ListItemHeight and add headerHeight, thes how you can get real absolute offset in scroll. If u got not header, position 0 will be listItem, so you can simplify absoluteY += pos*listItemHeight;

除了@jaredpetker 的回答。ListView 没有包含其滚动中的所有项目,因此您只需要操作列表的“可见”部分。当您向下滚动时,顶部项目会移出并作为新项目视图推送。这就是convertedView的来源(它不是要填充的空项目,它是列表“可见”部分之外的移动项目。所以你需要知道在可见部分之前有多少项目将它们与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;
}