40

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

有没有办法做到这一点?

4

8 回答 8

81

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

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

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

于 2012-05-30T00:14:49.110 回答
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;
}
于 2012-11-16T11:02:15.557 回答
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();
    }
}
于 2014-05-23T23:08:30.283 回答
12

首先声明您的 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);

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

于 2016-03-10T09:07:30.190 回答
9

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

于 2013-09-24T05:00:40.890 回答
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
        }
    }
);
于 2016-07-08T02:53:33.507 回答
2

除了@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;
}
于 2017-03-11T05:13:40.990 回答
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);
}

然后像这样在Main Activity中使用它。

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>
于 2016-08-31T15:01:32.560 回答