Android 返回到 ListView 时保持/保存/恢复滚动位置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3014089/
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 08:18:31  来源:igfitidea点击:

Maintain/Save/Restore scroll position when returning to a ListView

androidandroid-listviewscrollscroll-position

提问by rantravee

I have a long ListViewthat the user can scroll around before returning to the previous screen. When the user opens this ListViewagain, I want the list to be scrolled to the same point that it was previously. Any ideas on how to achieve this?

我有一个很长的时间ListView,用户可以在返回上一个屏幕之前滚动。当用户ListView再次打开它时,我希望列表滚动到与以前相同的点。关于如何实现这一目标的任何想法?

回答by ian

Try this:

尝试这个:

// save index and top position
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - mList.getPaddingTop());

// ...

// restore index and position
mList.setSelectionFromTop(index, top);

Explanation:

ListView.getFirstVisiblePosition()returns the top visible list item. But this item may be partially scrolled out of view, and if you want to restore the exact scroll position of the list you need to get this offset. So ListView.getChildAt(0)returns the Viewfor the top list item, and then View.getTop() - mList.getPaddingTop()returns its relative offset from the top of the ListView. Then, to restore the ListView's scroll position, we call ListView.setSelectionFromTop()with the index of the item we want and an offset to position its top edge from the top of the ListView.

说明:

ListView.getFirstVisiblePosition()返回顶部可见的列表项。但是这个 item 可能会部分滚动到视图之外,如果你想恢复列表的确切滚动位置,你需要得到这个偏移量。因此,ListView.getChildAt(0)返回View顶部列表项的 ,然后View.getTop() - mList.getPaddingTop()返回其相对于 顶部的相对偏移量ListView。然后,为了恢复ListView的滚动位置,我们ListView.setSelectionFromTop()使用我们想要的项目的索引和偏移量进行调用,以将其顶部边缘定位到ListView.

回答by Eugene Mymrin

Parcelable state;

@Override
public void onPause() {    
    // Save ListView state @ onPause
    Log.d(TAG, "saving listview state");
    state = listView.onSaveInstanceState();
    super.onPause();
}
...

@Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    // Set new items
    listView.setAdapter(adapter);
    ...
    // Restore previous state (including selected item index and scroll position)
    if(state != null) {
        Log.d(TAG, "trying to restore listview state");
        listView.onRestoreInstanceState(state);
    }
}

回答by Giorgio Barchiesi

I adopted the solution suggested by @(Kirk Woll), and it works for me. I have also seen in the Android source code for the "Contacts" app, that they use a similar technique. I would like to add some more details: On top on my ListActivity-derived class:

我采用了@(Kirk Woll) 建议的解决方案,它对我有用。我还在“联系人”应用程序的 Android 源代码中看到,它们使用了类似的技术。我想添加更多细节:在我的 ListActivity 派生类的顶部:

private static final String LIST_STATE = "listState";
private Parcelable mListState = null;

Then, some method overrides:

然后,一些方法覆盖:

@Override
protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);
    mListState = state.getParcelable(LIST_STATE);
}

@Override
protected void onResume() {
    super.onResume();
    loadData();
    if (mListState != null)
        getListView().onRestoreInstanceState(mListState);
    mListState = null;
}

@Override
protected void onSaveInstanceState(Bundle state) {
    super.onSaveInstanceState(state);
    mListState = getListView().onSaveInstanceState();
    state.putParcelable(LIST_STATE, mListState);
}

Of course "loadData" is my function to retrieve data from the DB and put it onto the list.

当然,“loadData”是我从数据库中检索数据并将其放入列表的函数。

On my Froyo device, this works both when you change the phone orientation, and when you edit an item and go back to the list.

在我的 Froyo 设备上,这在您更改手机方向以及编辑项目并返回列表时都有效。

回答by Francesco Laurita

A very simple way:

一个非常简单的方法:

/** Save the position **/
int currentPosition = listView.getFirstVisiblePosition();

//Here u should save the currentPosition anywhere

/** Restore the previus saved position **/
listView.setSelection(savedPosition);

The method setSelectionwill reset the list to the supplied item. If not in touch mode the item will actually be selected if in touch mode the item will only be positioned on screen.

方法setSelection将列表重置为提供的项目。如果不在触摸模式下,项目将被实际选中,如果在触摸模式下,项目将仅位于屏幕上。

A more complicated approach:

更复杂的方法:

listView.setOnScrollListener(this);

//Implements the interface:
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
    mCurrentX = view.getScrollX();
    mCurrentY = view.getScrollY();
}

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {

}

//Save anywere the x and the y

/** Restore: **/
listView.scrollTo(savedX, savedY);

回答by shalafi

I found something interesting about this.

我发现了一些有趣的事情。

I tried setSelection and scrolltoXY but it did not work at all, the list remained in the same position, after some trial and error I got the following code that does work

我尝试了 setSelection 和 scrolltoXY 但它根本不起作用,列表保持在相同的位置,经过一些试验和错误后,我得到了以下有效的代码

final ListView list = (ListView) findViewById(R.id.list);
list.post(new Runnable() {            
    @Override
    public void run() {
        list.setSelection(0);
    }
});

If instead of posting the Runnable you try runOnUiThread it does not work either (at least on some devices)

如果不是发布 Runnable 你尝试 runOnUiThread 它也不起作用(至少在某些设备上)

This is a very strange workaround for something that should be straight forward.

对于应该直截了当的事情,这是一个非常奇怪的解决方法。

回答by aaronvargas

CAUTION!! There is a bug in AbsListView that doesn't allow the onSaveState() to work correctly if the ListView.getFirstVisiblePosition() is 0.

警告!!如果 ListView.getFirstVisiblePosition() 为 0,AbsListView 中有一个错误,它不允许 onSaveState() 正常工作。

So If you have large images that take up most of the screen, and you scroll to the second image, but a little of the first is showing, the scroll position Won't be saved...

因此,如果您有占据大部分屏幕的大图像,并且您滚动到第二张图像,但显示了第一张图像,则不会保存滚动位置...

from AbsListView.java:1650(comments mine)

来自AbsListView.java:1650(我的评论)

// this will be false when the firstPosition IS 0
if (haveChildren && mFirstPosition > 0) {
    ...
} else {
    ss.viewTop = 0;
    ss.firstId = INVALID_POSITION;
    ss.position = 0;
}

But in this situation, the 'top' in the code below will be a negative number which causes other issues that prevent the state to be restored correctly. So when the 'top' is negative, get the next child

但是在这种情况下,下面代码中的“顶部”将是一个负数,这会导致其他问题阻止正确恢复状态。所以当'top'为负时,得到下一个孩子

// save index and top position
int index = getFirstVisiblePosition();
View v = getChildAt(0);
int top = (v == null) ? 0 : v.getTop();

if (top < 0 && getChildAt(1) != null) {
    index++;
    v = getChildAt(1);
    top = v.getTop();
}
// parcel the index and top

// when restoring, unparcel index and top
listView.setSelectionFromTop(index, top);

回答by Leo Phan

private Parcelable state;
@Override
public void onPause() {
    state = mAlbumListView.onSaveInstanceState();
    super.onPause();
}

@Override
public void onResume() {
    super.onResume();

    if (getAdapter() != null) {
        mAlbumListView.setAdapter(getAdapter());
        if (state != null){
            mAlbumListView.requestFocus();
            mAlbumListView.onRestoreInstanceState(state);
        }
    }
}

That's enough

够了

回答by Ryan Newsom

For some looking for a solution to this problem, the root of the issue may be where you are setting your list views adapter. After you set the adapter on the listview, it resets the scroll position. Just something to consider. I moved setting the adapter into my onCreateView after we grab the reference to the listview, and it solved the problem for me. =)

对于一些寻找此问题解决方案的人来说,问题的根源可能在于您设置列表视图适配器的位置。在列表视图上设置适配器后,它会重置滚动位置。只是需要考虑的事情。在获取对列表视图的引用后,我将适配器设置移动到我的 onCreateView 中,它为我解决了问题。=)

回答by Michael Peterson

You can maintain the scroll state after a reload if you save the state before you reload and restore it after. In my case I made a asynchronous network request and reloaded the list in a callback after it completed. This is where I restore state. Code sample is Kotlin.

如果在重新加载之前保存状态并在之后恢复它,则可以在重新加载后保持滚动状态。就我而言,我提出了一个异步网络请求,并在完成后在回调中重新加载列表。这是我恢复状态的地方。代码示例是 Kotlin。

val state = myList.layoutManager.onSaveInstanceState()

getNewThings() { newThings: List<Thing> ->

    myList.adapter.things = newThings
    myList.layoutManager.onRestoreInstanceState(state)
}

回答by Mazvél

Am posting this because I am surprised nobody had mentioned this.

发布这个是因为我很惊讶没有人提到这个。

After user clicks the back button he will return to the listview in the same state as he went out of it.

用户单击后退按钮后,他将返回到与退出列表视图相同的状态。

This code will override the "up" button to behave the same way as the back button so in the case of Listview -> Details -> Back to Listview (and no other options) this is the simplest code to maintain the scrollposition and the content in the listview.

此代码将覆盖“向上”按钮,使其行为与后退按钮相同,因此在 Listview -> Details -> Back to Listview(没有其他选项)的情况下,这是维护滚动位置和内容的最简单代码在列表视图中。

 public boolean onOptionsItemSelected(MenuItem item) {
     switch (item.getItemId()) {
         case android.R.id.home:
             onBackPressed();
             return(true);
     }
     return(super.onOptionsItemSelected(item)); }

Caution: If you can go to another activity from the details activity the up button will return you back to that activity so you will have to manipulate the backbutton history in order for this to work.

注意:如果您可以从详细信息活动转到另一个活动,向上按钮将使您返回到该活动,因此您必须操纵后退按钮历史记录才能使其工作。