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

ran*_*vee 388 android scroll scroll-position android-listview

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

ian*_*ian 628

试试这个:

// 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);
Run Code Online (Sandbox Code Playgroud)

说明:

ListView.getFirstVisiblePosition()返回顶部可见列表项.但是此项可能会部分滚动到视图之外,如果要恢复列表的确切滚动位置,则需要获取此偏移量.因此ListView.getChildAt(0)返回View顶部列表项,然后View.getTop() - mList.getPaddingTop()从顶部返回其相对偏移量ListView.然后,为了恢复ListView滚动位置,我们调用ListView.setSelectionFromTop()我们想要的项目的索引和一个偏移来定位它的顶部边缘ListView.

  • 因此ListView.getFirstVisiblePosition()返回顶部可见列表项.但是此项可能会部分滚动到视图之外,如果要恢复列表的确切滚动位置,则需要获取此偏移量.因此ListView.getChildAt(0)返回顶部列表项的View,然后View.getTop()返回其从ListView顶部的相对偏移量.然后,为了恢复ListView的滚动位置,我们使用我们想要的项目的索引调用ListView.setSelectionFromTop(),并使用偏移量将其顶边从ListView的顶部定位.全清? (56认同)
  • @Phil你的简单解决方案不能恢复精确的滚动位置. (17认同)
  • 这可以更简单地使用一行来保存:`int index = mList.getFirstVisiblePosition();`并且只有一行要恢复:`mList.setSelectionFromTop(index,0);`.很棒的答案(+1)!我一直在寻找这个问题的优雅解决方案. (15认同)
  • 不太了解这是如何工作的.我只使用listView.getFirstVisiblePosition()并理解,但不知道这里发生了什么.有没有简要解释的机会?:) (2认同)
  • 我认为你还需要考虑ListView顶部填充... (2认同)
  • 正如@nbarraille所说,代码应该更像"v.getTop() - mList.getPaddingTop()".否则你将花费一个小时,就像我试图弄清楚为什么它总能恢复只是一头脱发... (2认同)
  • 如果列表视图具有页眉和页脚,则不起作用. (2认同)

Eug*_*rin 535

Parcelable state;

@Override
public void onPause() {    
    // Save ListView state @ onPause
    Log.d(TAG, "saving listview state @ onPause");
    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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • **我认为这是最佳答案**,因为此方法可以恢复精确的滚动位置,而不仅仅是第一个可见项目. (104认同)
  • 好的解决方案 只是一个问题.如果项目很大并且您滚动,但第一项仍然可见,则列表会跳回到顶部.如果您滚动到第二个项目或其他任何地方一切正常 (12认同)
  • 很好的答案,但在动态加载内容并且您想要在onPause()方法中保存状态时不起作用. (9认同)
  • @passsy你有没有找到一个解决方案列表跳回到顶部? (3认同)
  • 正如 aaronvargas 所说,当 ListView.getFirstVisiblePosition() 为 0 时,这不起作用。 (2认同)
  • 只有当我将`Parcelable state`设置为**`static`时才有效** (2认同)

Gio*_*esi 54

我采用了@(Kirk Woll)建议的解决方案,它对我有用.我还在"联系人"应用程序的Android源代码中看到,他们使用了类似的技术.我想补充一些细节:在我的ListActivity派生类的顶部:

private static final String LIST_STATE = "listState";
private Parcelable mListState = null;
Run Code Online (Sandbox Code Playgroud)

然后,一些方法覆盖:

@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);
}
Run Code Online (Sandbox Code Playgroud)

当然"loadData"是我从DB中检索数据并将其放入列表的功能.

在我的Froyo设备上,当您更改手机方向和编辑项目并返回列表时,这既适用也适用.

  • 如果你在一个片段中使用它(我正在使用列表片段)并导航到其他片段,这将会崩溃,因为在调用`mListState = getListView().onSaveInstanceState()时不会创建内容视图.`主要在设备上回转. (2认同)
  • 永远不会调用`onRestoreInstanceState` :( (2认同)

Fra*_*ita 26

一个非常简单的方法:

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

//Here u should save the currentPosition anywhere

/** Restore the previus saved position **/
listView.setSelection(savedPosition);
Run Code Online (Sandbox Code Playgroud)

方法setSelection将列表重置为提供的项目.如果未处于触摸模式,则实际上将选择该项目,如果在触摸模式下该项目将仅位于屏幕上.

一种更复杂的方法:

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);
Run Code Online (Sandbox Code Playgroud)

  • view.getScrollX()和view.getScrollY()总是返回0! (27认同)
  • 使用View.getChildAt()和View.getTop()查看上面的(已接受的)解决方案.您不能在ListView上使用View.getScrollY(),因为ListView在内部维护其滚动. (3认同)
  • 你的答案有错误.该方法称为setSelection (2认同)

sha*_*afi 17

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

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

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

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

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

  • listview.post在我的Galaxy Note 4.0.4上工作正常,但在我的Nexus One 2.3.6上不起作用.但是,onOnUIThread(action)在两个设备上都能正常工作. (2认同)

aar*_*gas 10

警告!!如果ListView.getFirstVisiblePosition()为0,则AbsListView中存在一个错误,该错误不允许onSaveState()正常工作.

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

来自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;
}
Run Code Online (Sandbox Code Playgroud)

但在这种情况下,下面代码中的"顶部"将是一个负数,这会导致阻止状态正确恢复的其他问题.因此,当"顶部"为负数时,请选择下一个孩子

// 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);
Run Code Online (Sandbox Code Playgroud)


小智 6

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

够了


Rya*_*som 6

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