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


当前回答

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

这就够了

其他回答

一个非常简单的方法:

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

//Here u should save the currentPosition anywhere

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

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

为了澄清Ryan Newsom的精彩回答并针对片段进行调整通常情况下,我们想要从主ListView片段导航到细节片段然后再返回主ListView片段

    private View root;
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
           if(root == null){
             root = inflater.inflate(R.layout.myfragmentid,container,false);
             InitializeView(); 
           } 
           return root; 
        }

    public void InitializeView()
    {
        ListView listView = (ListView)root.findViewById(R.id.listviewid);
        BaseAdapter adapter = CreateAdapter();//Create your adapter here
        listView.setAdpater(adapter);
        //other initialization code
    }

这里的“神奇”是,当我们从细节片段导航回ListView片段时,视图不会被重新创建,我们不设置ListView的适配器,所以一切都保持不变!

If you are saving/restoring scroll position of ListView yourself you are essentially duplicating the functionality already implemented in android framework. The ListView restores fine scroll position just well on its own except one caveat: as @aaronvargas mentioned there is a bug in AbsListView that won't let to restore fine scroll position for the first list item. Nevertheless the best way to restore scroll position is not to restore it. Android framework will do it better for you. Just make sure you have met the following conditions:

确保你没有调用setSaveEnabled(false)方法,也没有为xml布局文件中的列表设置android:saveEnabled="false"属性 为ExpandableListView重写long getCombinedChildId(long groupId, long childId)方法,使其返回正长数(BaseExpandableListAdapter类中的默认实现返回负数)。下面是一些例子:

.

@Override
public long getChildId(int groupPosition, int childPosition) {
    return 0L | groupPosition << 12 | childPosition;
}

@Override
public long getCombinedChildId(long groupId, long childId) {
    return groupId << 32 | childId << 1 | 1;
}

@Override
public long getGroupId(int groupPosition) {
    return groupPosition;
}

@Override
public long getCombinedGroupId(long groupId) {
    return (groupId & 0x7FFFFFFF) << 32;
}

如果在一个片段中使用了ListView或ExpandableListView,不要在活动重新创建片段(例如在屏幕旋转后)。使用findFragmentByTag(String标签)方法获取片段。 确保ListView有一个唯一的android:id。

To avoid aforementioned caveat with first list item you can craft your adapter the way it returns special dummy zero pixels height view for the ListView at position 0. Here is the simple example project shows ListView and ExpandableListView restore their fine scroll positions whereas their scroll positions are not explicitly saved/restored. Fine scroll position is restored perfectly even for the complex scenarios with temporary switching to some other application, double screen rotation and switching back to the test application. Please note, if you are explicitly exiting the application (by pressing the Back button) the scroll position won't be saved (as well as all other Views won't save their state). https://github.com/voromto/RestoreScrollPosition/releases

试试这个:

// 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 View for 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.

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