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

时间:2012-05-30 00:06:38

标签: android android-listview

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

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

8 个答案:

答案 0 :(得分:79)

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

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

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

答案 1 :(得分: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;
}

答案 2 :(得分: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();
    }
}

答案 3 :(得分:9)

如果其他人在Google中发现这一点,同时寻找一种方法来跟踪OnScrollListener中的 relative 滚动偏移量 - 也就是说,自上次调用侦听器后更改为Y - here's a Gist showing how to calculate that

答案 4 :(得分:6)

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

我在安装后使用了我的适配器,对我来说很好..

答案 5 :(得分: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,每次调用onScroll时都会调用onScrollPositionChanged方法。但是,此方法可以访问表示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
        }
    }
);

答案 6 :(得分:1)

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

首先创建班级

string name = ...
int regionId = ...
var customers = connection.Query<Customer>(
    "select * from Customers where Name = @name and RegionId = @regionId",
    new { name, regionId }).AsList();

然后在主要活动中使用它。

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);
}
App Gradle中的

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

    }
}

在XML布局中

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>

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

答案 7 :(得分:1)

除了@jaredpetker回答。 ListView没有保存其滚动条中的所有项目,因此您只需要操作&#34;可见&#34;列表的一部分。当您向下滚动时,顶部项目会被移出并作为新项目视图推送。这就是转换视图是如何来的(它不是空的项目来填充,它的移动项目是&#34;可见&#34;列表的一部分。所以你需要知道有多少项目是在可见部分与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;
}