如何在 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

  1. 让你的 'Home' pojo class 实现 Parcelable.
  2. 将 List mHomeList 更改为 ArrayList mHomeList。
  3. 在您的片段上实施 onSaveInstanceState(Bundle outState) 并将 mHomeList 放入 sate bundle。

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelableArrayList("STATE_HOME_LIST", mHomeList);
    }
    
  4. 在 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());
}