如何在 Fragment 的 onDestroyView 中保存 List?
How to save List in onDestroyView in Fragment?
我正在尝试实现类似 Instagram 的用户个人资料屏幕。这是我的流程ProfileFragment --> SingleImageFragment --> ProfileFragment --> SingleImageFragment
。因此,用户可以拥有相同 fragment
的多个实例。我不想 hide/show 因为它会占用太多内存。所以我正在使用
mFragment = fragment;
mFragmentManager
.beginTransaction()
.replace(R.id.frame_container, fragment, Integer.toString(getFragmentCount()))
.addToBackStack(null)
.commit();
但是,这种方式 ProfileFragment
进入 onDestroyView
并且当它从 backStack
返回时,列表将被再次加载,因为它进入 onViewCreated
方法。我正在尝试找到一种方法如何在到达 fragment
时不加载已经加载的数据。有没有一种方法可以保存已加载的列表并在 onViewCreated
?
中使用该列表
这是ProfileFragment
:
public class ProfileFragment extends BaseChildFragment implements ProfileAdapter.OnHeaderItemClickedListener,
SwipeRefreshLayout.OnRefreshListener {
public static final String ACTION_SINGLE_IMAGE = ProfileFragment.class.getName() + ".single_image";
public static final String ACTION_SETTINGS = ProfileFragment.class.getName() + ".settings";
private static final String ARG_PARAM1 = "param1";
FragmentProfileBinding mBinder;
FragmentManager mFragmentManager;
Home mHome;
Home tHome;
List<Home> mHomeList;
ProfileAdapter mAdapter;
StaggeredGridLayoutManager mManager;
EndlessRecyclerViewScrollListener mEndlessScrollListener;
PreferenceAdapter mPreferenceAdapter;
int mInstance;
public ProfileFragment() {
}
public static ProfileFragment newInstance(int instance) {
ProfileFragment fragment = new ProfileFragment();
Bundle args = new Bundle();
args.putInt(ARGS_INSTANCE, instance);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mInstance = getArguments().getInt(ARGS_INSTANCE);
}
mHomeList = new ArrayList<>();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mBinder = DataBindingUtil.inflate(inflater, R.layout.fragment_profile, container, false);
mFragmentManager = getChildFragmentManager();
mHome = new Home();
tHome = new Home();
mPreferenceAdapter = new PreferenceAdapter(getContext());
mManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
mAdapter = new ProfileAdapter(getContext(), mHomeList);
mBinder.rvGrid.setLayoutManager(mManager);
mBinder.rvGrid.setAdapter(mAdapter);
mAdapter.addOnHeaderItemClickListener(this);
mBinder.srLayout.setOnRefreshListener(this);
mEndlessScrollListener = new EndlessRecyclerViewScrollListener(mManager) {
@Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
populateList();
populateSecondList();
mAdapter.notifyDataSetChanged();
}
};
mBinder.rvGrid.addOnScrollListener(mEndlessScrollListener);
setUIListeners();
return mBinder.getRoot();
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
populateList();
populateSecondList();
}
@Override
public void onDestroyView() {
super.onDestroyView();
mAdapter.removeOnHeaderItemClickListener();
mBinder.rvGrid.removeOnScrollListener(mEndlessScrollListener);
}
private void setUIListeners() {
}
@Override
public void onGridImageClicked(Home home) {
sendActionToParent(ACTION_SINGLE_IMAGE);
}
@Override
public void onRefresh() {
mHomeList.clear();
populateList();
populateSecondList();
mAdapter.notifyDataSetChanged();
mEndlessScrollListener.resetState();
mBinder.srLayout.setRefreshing(false);
}
private void sendActionToParent(String action) {
if (mParentListener == null) {
return;
}
Bundle bundle = new Bundle();
bundle.putString(Constants.ACTION_KEY, action);
mParentListener.onChildFragmentInteraction(bundle);
}
private void sendActionToActivity(String action) {
if (mActivityListener == null) {
return;
}
Bundle bundle = new Bundle();
bundle.putString(Constants.ACTION_KEY, action);
mActivityListener.onChildFragmentToActivityInteraction(bundle);
}
private void populateList() {
for (int i = 0; i < 5; i++) {
mHome.setType(0);
mHomeList.add(i, mHome);
}
}
private void populateSecondList() {
for (int i = 0; i < 5; i++) {
tHome.setType(1);
mHomeList.add(i, tHome);
}
}
}
是的,您可以尝试使用 setRetainInstance(true);
(在您的 onCreate()
方法中添加这一行)来保留您的 Fragment
.
的状态
或者您可以尝试使用 SQLite
或 ORM 框架将列表保存到数据库中。我建议您使用 ORMLite
来保存您的列表:
要在您的项目中使用 ORMLite
,请将此行添加到您的 gradle.build
文件 (module:app)
compile group: 'com.j256.ormlite', name: 'ormlite-android', version: '4.45'
然后,您必须创建一个包装器 class 并将您的列表放入其中:
@DatabaseTable(tableName = "your_table_name")
public class YourClass{
@DatabaseField(generatedId = true)
private int id;
@DatabaseField(dataType = DataType.SERIALIZABLE)
private List<YourList> list;
//getters and setters here
}
现在,您必须创建 DatabaseHelper
:
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
// name of the database file for your application -- change to something appropriate for your app
private static final String DATABASE_NAME = "yourdatabasename.db";
// any time you make changes to your database objects, you may have to increase the database version
private static final int DATABASE_VERSION = 1;
// the DAO object we use to access the SimpleData table
private Dao<YourClass, Integer> simpleDao = null;
private RuntimeExceptionDao<YourClass, Integer> simpleRuntimeDao = null;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION, R.raw.ormlite_config);
}
/**
* This is called when the database is first created. Usually you should call createTable statements here to create
* the tables that will store your data.
*/
@Override
public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
try {
Log.i(DatabaseHelper.class.getName(), "onCreate");
TableUtils.createTable(connectionSource, SimpleData.class);
} catch (SQLException e) {
Log.e(DatabaseHelper.class.getName(), "Can't create database", e);
throw new RuntimeException(e);
}
// here we try inserting data in the on-create as a test
RuntimeExceptionDao<YourClass, Integer> dao = getSimpleDataDao();
long millis = System.currentTimeMillis();
// create some entries in the onCreate
YourClass simple = new YourClass();
dao.create(simple);
simple = new YourClass();
dao.create(simple);
Log.i(DatabaseHelper.class.getName(), "created new entries in onCreate: " + millis);
}
/**
* This is called when your application is upgraded and it has a higher version number. This allows you to adjust
* the various data to match the new version number.
*/
@Override
public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) {
try {
Log.i(DatabaseHelper.class.getName(), "onUpgrade");
TableUtils.dropTable(connectionSource, SimpleData.class, true);
// after we drop the old databases, we create the new ones
onCreate(db, connectionSource);
} catch (SQLException e) {
Log.e(DatabaseHelper.class.getName(), "Can't drop databases", e);
throw new RuntimeException(e);
}
}
/**
* Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached
* value.
*/
public Dao<YourClass, Integer> getDao() throws SQLException {
if (simpleDao == null) {
simpleDao = getDao(SimpleData.class);
}
return simpleDao;
}
/**
* Returns the RuntimeExceptionDao (Database Access Object) version of a Dao for our SimpleData class. It will
* create it or just give the cached value. RuntimeExceptionDao only through RuntimeExceptions.
*/
public RuntimeExceptionDao<SimpleData, Integer> getSimpleDataDao() {
if (simpleRuntimeDao == null) {
simpleRuntimeDao = getRuntimeExceptionDao(SimpleData.class);
}
return simpleRuntimeDao;
}
/**
* Close the database connections and clear any cached DAOs.
*/
@Override
public void close() {
super.close();
simpleDao = null;
simpleRuntimeDao = null;
}
最后,保存您的列表并在需要时恢复它:
//To query all:
Yourclass class = simpleDao.queryForAll();
//To save data:
simpleDao.create(your_object_whit_lists);
使用 savedInstanceState for the example
- 让你的 'Home' pojo class 实现 Parcelable.
- 将 List mHomeList 更改为 ArrayList mHomeList。
在您的片段上实施 onSaveInstanceState(Bundle outState) 并将 mHomeList 放入 sate bundle。
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelableArrayList("STATE_HOME_LIST", mHomeList);
}
在 onViewCreated() 中,您可以像这样恢复您的 mHomeList
if (savedInstanceState != null) {
mHomeList = savedInstanceState.getParcelableArrayList("STATE_HOME_LIST");
// do something // update Adapter
}else {
populateList();
populateSecondList();
}
根据 this 问题的第二个答案,我想出了这个解决方案。虽然我不知道它会有多可靠。
我把列表保存在Bundle
:
private Bundle saveState() {
Bundle state = new Bundle();
Parcelable parcelable = Parcels.wrap(mHomeList);
state.putParcelable("homeList", parcelable);
return state;
}
然后在onViewCreated
:
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedState != null) {
mHomeList = Parcels.unwrap(savedState.getParcelable("homeList"));
} else {
populateList();
populateSecondList();
}
savedState = null;
Log.d("TAG", "PROFILE: " + mHomeList.size());
}
我正在尝试实现类似 Instagram 的用户个人资料屏幕。这是我的流程ProfileFragment --> SingleImageFragment --> ProfileFragment --> SingleImageFragment
。因此,用户可以拥有相同 fragment
的多个实例。我不想 hide/show 因为它会占用太多内存。所以我正在使用
mFragment = fragment;
mFragmentManager
.beginTransaction()
.replace(R.id.frame_container, fragment, Integer.toString(getFragmentCount()))
.addToBackStack(null)
.commit();
但是,这种方式 ProfileFragment
进入 onDestroyView
并且当它从 backStack
返回时,列表将被再次加载,因为它进入 onViewCreated
方法。我正在尝试找到一种方法如何在到达 fragment
时不加载已经加载的数据。有没有一种方法可以保存已加载的列表并在 onViewCreated
?
这是ProfileFragment
:
public class ProfileFragment extends BaseChildFragment implements ProfileAdapter.OnHeaderItemClickedListener,
SwipeRefreshLayout.OnRefreshListener {
public static final String ACTION_SINGLE_IMAGE = ProfileFragment.class.getName() + ".single_image";
public static final String ACTION_SETTINGS = ProfileFragment.class.getName() + ".settings";
private static final String ARG_PARAM1 = "param1";
FragmentProfileBinding mBinder;
FragmentManager mFragmentManager;
Home mHome;
Home tHome;
List<Home> mHomeList;
ProfileAdapter mAdapter;
StaggeredGridLayoutManager mManager;
EndlessRecyclerViewScrollListener mEndlessScrollListener;
PreferenceAdapter mPreferenceAdapter;
int mInstance;
public ProfileFragment() {
}
public static ProfileFragment newInstance(int instance) {
ProfileFragment fragment = new ProfileFragment();
Bundle args = new Bundle();
args.putInt(ARGS_INSTANCE, instance);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mInstance = getArguments().getInt(ARGS_INSTANCE);
}
mHomeList = new ArrayList<>();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mBinder = DataBindingUtil.inflate(inflater, R.layout.fragment_profile, container, false);
mFragmentManager = getChildFragmentManager();
mHome = new Home();
tHome = new Home();
mPreferenceAdapter = new PreferenceAdapter(getContext());
mManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
mAdapter = new ProfileAdapter(getContext(), mHomeList);
mBinder.rvGrid.setLayoutManager(mManager);
mBinder.rvGrid.setAdapter(mAdapter);
mAdapter.addOnHeaderItemClickListener(this);
mBinder.srLayout.setOnRefreshListener(this);
mEndlessScrollListener = new EndlessRecyclerViewScrollListener(mManager) {
@Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
populateList();
populateSecondList();
mAdapter.notifyDataSetChanged();
}
};
mBinder.rvGrid.addOnScrollListener(mEndlessScrollListener);
setUIListeners();
return mBinder.getRoot();
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
populateList();
populateSecondList();
}
@Override
public void onDestroyView() {
super.onDestroyView();
mAdapter.removeOnHeaderItemClickListener();
mBinder.rvGrid.removeOnScrollListener(mEndlessScrollListener);
}
private void setUIListeners() {
}
@Override
public void onGridImageClicked(Home home) {
sendActionToParent(ACTION_SINGLE_IMAGE);
}
@Override
public void onRefresh() {
mHomeList.clear();
populateList();
populateSecondList();
mAdapter.notifyDataSetChanged();
mEndlessScrollListener.resetState();
mBinder.srLayout.setRefreshing(false);
}
private void sendActionToParent(String action) {
if (mParentListener == null) {
return;
}
Bundle bundle = new Bundle();
bundle.putString(Constants.ACTION_KEY, action);
mParentListener.onChildFragmentInteraction(bundle);
}
private void sendActionToActivity(String action) {
if (mActivityListener == null) {
return;
}
Bundle bundle = new Bundle();
bundle.putString(Constants.ACTION_KEY, action);
mActivityListener.onChildFragmentToActivityInteraction(bundle);
}
private void populateList() {
for (int i = 0; i < 5; i++) {
mHome.setType(0);
mHomeList.add(i, mHome);
}
}
private void populateSecondList() {
for (int i = 0; i < 5; i++) {
tHome.setType(1);
mHomeList.add(i, tHome);
}
}
}
是的,您可以尝试使用 setRetainInstance(true);
(在您的 onCreate()
方法中添加这一行)来保留您的 Fragment
.
或者您可以尝试使用 SQLite
或 ORM 框架将列表保存到数据库中。我建议您使用 ORMLite
来保存您的列表:
要在您的项目中使用 ORMLite
,请将此行添加到您的 gradle.build
文件 (module:app)
compile group: 'com.j256.ormlite', name: 'ormlite-android', version: '4.45'
然后,您必须创建一个包装器 class 并将您的列表放入其中:
@DatabaseTable(tableName = "your_table_name")
public class YourClass{
@DatabaseField(generatedId = true)
private int id;
@DatabaseField(dataType = DataType.SERIALIZABLE)
private List<YourList> list;
//getters and setters here
}
现在,您必须创建 DatabaseHelper
:
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
// name of the database file for your application -- change to something appropriate for your app
private static final String DATABASE_NAME = "yourdatabasename.db";
// any time you make changes to your database objects, you may have to increase the database version
private static final int DATABASE_VERSION = 1;
// the DAO object we use to access the SimpleData table
private Dao<YourClass, Integer> simpleDao = null;
private RuntimeExceptionDao<YourClass, Integer> simpleRuntimeDao = null;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION, R.raw.ormlite_config);
}
/**
* This is called when the database is first created. Usually you should call createTable statements here to create
* the tables that will store your data.
*/
@Override
public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
try {
Log.i(DatabaseHelper.class.getName(), "onCreate");
TableUtils.createTable(connectionSource, SimpleData.class);
} catch (SQLException e) {
Log.e(DatabaseHelper.class.getName(), "Can't create database", e);
throw new RuntimeException(e);
}
// here we try inserting data in the on-create as a test
RuntimeExceptionDao<YourClass, Integer> dao = getSimpleDataDao();
long millis = System.currentTimeMillis();
// create some entries in the onCreate
YourClass simple = new YourClass();
dao.create(simple);
simple = new YourClass();
dao.create(simple);
Log.i(DatabaseHelper.class.getName(), "created new entries in onCreate: " + millis);
}
/**
* This is called when your application is upgraded and it has a higher version number. This allows you to adjust
* the various data to match the new version number.
*/
@Override
public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) {
try {
Log.i(DatabaseHelper.class.getName(), "onUpgrade");
TableUtils.dropTable(connectionSource, SimpleData.class, true);
// after we drop the old databases, we create the new ones
onCreate(db, connectionSource);
} catch (SQLException e) {
Log.e(DatabaseHelper.class.getName(), "Can't drop databases", e);
throw new RuntimeException(e);
}
}
/**
* Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached
* value.
*/
public Dao<YourClass, Integer> getDao() throws SQLException {
if (simpleDao == null) {
simpleDao = getDao(SimpleData.class);
}
return simpleDao;
}
/**
* Returns the RuntimeExceptionDao (Database Access Object) version of a Dao for our SimpleData class. It will
* create it or just give the cached value. RuntimeExceptionDao only through RuntimeExceptions.
*/
public RuntimeExceptionDao<SimpleData, Integer> getSimpleDataDao() {
if (simpleRuntimeDao == null) {
simpleRuntimeDao = getRuntimeExceptionDao(SimpleData.class);
}
return simpleRuntimeDao;
}
/**
* Close the database connections and clear any cached DAOs.
*/
@Override
public void close() {
super.close();
simpleDao = null;
simpleRuntimeDao = null;
}
最后,保存您的列表并在需要时恢复它:
//To query all:
Yourclass class = simpleDao.queryForAll();
//To save data:
simpleDao.create(your_object_whit_lists);
使用 savedInstanceState for the example
- 让你的 'Home' pojo class 实现 Parcelable.
- 将 List mHomeList 更改为 ArrayList mHomeList。
在您的片段上实施 onSaveInstanceState(Bundle outState) 并将 mHomeList 放入 sate bundle。
@Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelableArrayList("STATE_HOME_LIST", mHomeList); }
在 onViewCreated() 中,您可以像这样恢复您的 mHomeList
if (savedInstanceState != null) { mHomeList = savedInstanceState.getParcelableArrayList("STATE_HOME_LIST"); // do something // update Adapter }else { populateList(); populateSecondList(); }
根据 this 问题的第二个答案,我想出了这个解决方案。虽然我不知道它会有多可靠。
我把列表保存在Bundle
:
private Bundle saveState() {
Bundle state = new Bundle();
Parcelable parcelable = Parcels.wrap(mHomeList);
state.putParcelable("homeList", parcelable);
return state;
}
然后在onViewCreated
:
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedState != null) {
mHomeList = Parcels.unwrap(savedState.getParcelable("homeList"));
} else {
populateList();
populateSecondList();
}
savedState = null;
Log.d("TAG", "PROFILE: " + mHomeList.size());
}