我有一个很长的ListView,用户可以在返回前一个屏幕之前滚动它。当用户再次打开这个ListView时,我希望列表被滚动到与之前相同的位置。关于如何实现这一点,你有什么想法吗?
当前回答
这里提供的解决方案似乎都不适合我。在我的情况下,我有一个ListView在一个片段,我替换在一个FragmentTransaction,所以一个新的片段实例创建每次片段显示,这意味着ListView状态不能存储为片段的成员。
相反,我最终将状态存储在我的自定义Application类中。下面的代码应该会让你了解它是如何工作的:
public class MyApplication extends Application {
public static HashMap<String, Parcelable> parcelableCache = new HashMap<>();
/* ... code omitted for brevity ... */
}
public class MyFragment extends Fragment{
private ListView mListView = null;
private MyAdapter mAdapter = null;
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mAdapter = new MyAdapter(getActivity(), null, 0);
mListView = ((ListView) view.findViewById(R.id.myListView));
Parcelable listViewState = MyApplication.parcelableCache.get("my_listview_state");
if( listViewState != null )
mListView.onRestoreInstanceState(listViewState);
}
@Override
public void onPause() {
MyApplication.parcelableCache.put("my_listview_state", mListView.onSaveInstanceState());
super.onPause();
}
/* ... code omitted for brevity ... */
}
基本思想是将状态存储在片段实例之外。如果您不喜欢在应用程序类中拥有静态字段的想法,我猜您可以通过实现一个片段接口并将状态存储在您的活动中来实现它。
另一种解决方案是将其存储在SharedPreferences中,但这有点复杂,您需要确保在应用程序启动时清除它,除非您希望在应用程序启动时保持状态。
另外,为了避免“当第一项可见时滚动位置不保存”,你可以显示一个0px高度的虚拟第一项。这可以通过重写适配器中的getView()来实现,如下所示:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if( position == 0 ) {
View zeroHeightView = new View(parent.getContext());
zeroHeightView.setLayoutParams(new ViewGroup.LayoutParams(0, 0));
return zeroHeightView;
}
else
return super.getView(position, convertView, parent);
}
其他回答
这里提供的解决方案似乎都不适合我。在我的情况下,我有一个ListView在一个片段,我替换在一个FragmentTransaction,所以一个新的片段实例创建每次片段显示,这意味着ListView状态不能存储为片段的成员。
相反,我最终将状态存储在我的自定义Application类中。下面的代码应该会让你了解它是如何工作的:
public class MyApplication extends Application {
public static HashMap<String, Parcelable> parcelableCache = new HashMap<>();
/* ... code omitted for brevity ... */
}
public class MyFragment extends Fragment{
private ListView mListView = null;
private MyAdapter mAdapter = null;
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mAdapter = new MyAdapter(getActivity(), null, 0);
mListView = ((ListView) view.findViewById(R.id.myListView));
Parcelable listViewState = MyApplication.parcelableCache.get("my_listview_state");
if( listViewState != null )
mListView.onRestoreInstanceState(listViewState);
}
@Override
public void onPause() {
MyApplication.parcelableCache.put("my_listview_state", mListView.onSaveInstanceState());
super.onPause();
}
/* ... code omitted for brevity ... */
}
基本思想是将状态存储在片段实例之外。如果您不喜欢在应用程序类中拥有静态字段的想法,我猜您可以通过实现一个片段接口并将状态存储在您的活动中来实现它。
另一种解决方案是将其存储在SharedPreferences中,但这有点复杂,您需要确保在应用程序启动时清除它,除非您希望在应用程序启动时保持状态。
另外,为了避免“当第一项可见时滚动位置不保存”,你可以显示一个0px高度的虚拟第一项。这可以通过重写适配器中的getView()来实现,如下所示:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if( position == 0 ) {
View zeroHeightView = new View(parent.getContext());
zeroHeightView.setLayoutParams(new ViewGroup.LayoutParams(0, 0));
return zeroHeightView;
}
else
return super.getView(position, convertView, parent);
}
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);
}
}
难道不是简单的android:saveEnabled="true"在ListView xml声明足够吗?
为了澄清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的适配器,所以一切都保持不变!
如果在重新加载前保存状态,并在重新加载后恢复状态,则可以在重新加载后保持滚动状态。在我的情况下,我做了一个异步网络请求,并在它完成后在回调中重新加载列表。这是我恢复状态的地方。代码示例是Kotlin。
val state = myList.layoutManager.onSaveInstanceState()
getNewThings() { newThings: List<Thing> ->
myList.adapter.things = newThings
myList.layoutManager.onRestoreInstanceState(state)
}
推荐文章
- apk (.apk)和应用程序包(.aab)的区别
- 如何设置超时在改造库?
- Android - SPAN_EXCLUSIVE_EXCLUSIVE跨度不能为零长度
- TextView的字体大小在Android应用程序改变字体大小从本机设置
- 如何模拟Android杀死我的进程
- 禁用EditText闪烁光标
- Android Eclipse -无法找到*.apk
- 设置TextView文本从html格式的字符串资源在XML
- 如何允许所有网络连接类型HTTP和HTTPS在Android(9)馅饼?
- Android加载JS包失败
- Android Studio, logcat在应用程序关闭后清理
- 如何使用jQuery检测页面的滚动位置
- 在android中从上下文获取活动
- 无法解析主机"<URL here>"没有与主机名关联的地址
- getActivity()在Fragment函数中返回null