【问题标题】:How to save List in onDestroyView in Fragment?如何在 Fragment 的 onDestroyView 中保存 List?
【发布时间】:2017-04-25 11:42:59
【问题描述】:

我正在尝试实现类似 Instagram 的用户个人资料屏幕。这是我的流程ProfileFragment --> SingleImageFragment --> ProfileFragment --> SingleImageFragment。因此,用户可以拥有相同fragment 的多个实例。我不想隐藏/显示,因为它消耗太多内存。所以我正在使用

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

【问题讨论】:

    标签: android android-fragments fragment-backstack


    【解决方案1】:

    是的,您可以尝试使用setRetainInstance(true);(在您的onCreate() 方法中添加此行)来保留您的Fragment 的状态。

    或者您可以尝试使用SQLite 或ORM 框架将列表保存到数据库中。我建议您使用ORMLite 来保存您的列表:

    要在您的项目中使用ORMLite,请将此行添加到您的gradle.build 文件(模块:app)中

    compile group: 'com.j256.ormlite', name: 'ormlite-android', version: '4.45'
    

    然后,您必须创建一个包装类并将您的列表放入其中:

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

    【讨论】:

    • 使用setRetainInstance(true),当我转到另一个片段时,生命周期转到onDestroyView。当我从backStack 回到它时,它再次进入onViewCreated,所以我的列表永远不会保存。
    • 在这种情况下,您必须使用 2 选项,使用 SQLite 或 ORMLite
    • 嗯,但是我必须为每个片段存储单独的列表,并在片段被销毁时删除它们。因为我可能有 4 个 ProfileFragment 实例,每个实例都有不同的列表。
    【解决方案2】:

    使用 savedInstanceState for the example

    1. 让你的'Home' pojo 类实现Parcelable
    2. 将列表 mHomeList 更改为 ArrayList mHomeList。
    3. 在您的片段上实现 onSaveInstanceState(Bundle outState) 并将 mHomeList 放入包中。

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

    【讨论】:

    • 通过替换同一活动中的片段,片段永远不会调用onSaveInstanceState。我需要将列表保存为parcelable,但如果从未调用过onSaveInstanceState,我应该将其保存在哪里?
    【解决方案3】:

    基于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());
    }
    

    【讨论】:

      猜你喜欢
      • 2013-09-06
      • 2014-12-09
      • 1970-01-01
      • 2021-10-11
      • 2020-10-20
      • 1970-01-01
      • 2018-02-13
      • 2016-07-29
      • 1970-01-01
      相关资源
      最近更新 更多