【问题标题】:Better way/place to store state data (listview)存储状态数据的更好方式/位置(列表视图)
【发布时间】:2012-08-16 04:49:11
【问题描述】:

我对 Android 比较陌生,而且我对列表、片段以及在哪里存储一些状态数据有一些疑问。 我在一个示例中工作,其中我有一个使用片段的列表视图和一个详细视图。 当我第一次打开应用程序时,我(从 Web 服务)加载项目列表(使用 asyntask)。 我想“保存”那个列表,所以如果我需要回到这个活动(列表),我不需要再次执行异步任务。 在哪里“保存”这个列表更好? Application 对象是个好地方吗?

然后,当我从列表中单击一个项目时,我想打开一个新活动并从该对象加载详细数据。 将该对象传递给详细信息活动的最佳方式是什么? 使用 Application 对象并从列表中获取所选项目(例如使用来自 onItemSelectedListener 的位置参数)(如果我有一个包含应用程序对象中项目的列表)? 让我的“项目”对象实现 Parcelable 接口并将整个对象传递给意图? 还有其他想法吗?

感谢和抱歉我的英语。

【问题讨论】:

    标签: android


    【解决方案1】:

    如果您想永久保存数据,SQLite 是正确的选择。

    如果你想临时缓存数据,saveInstanceState Bundle 就是为你准备的。我向您展示了一个使用 Fragment 和 ListView 的示例。

    public static final String BUNDLE_CACHE = "ListFragment.BUNDLE_CACHE";
    
    private ArrayList<ListItem> mCachedData;
    private ListView mListView;
    private ListItemAdapter mListAdapter;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        if ((savedInstanceState != null) && savedInstanceState.containsKey(BUNDLE_CACHE)) {
            this.mCachedData = savedInstanceState.getParcelableArrayList(BUNDLE_CACHE);
        }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
    
        LinearLayout layout = (LinearLayout) inflater.inflate(
                R.layout.layout_fragment, null);
    
        this.mListAdapter = new ListAdapter(inflater.getContext(), 
                R.layout.item_list_topic_categories);
    
        this.mListView = (ListView) layout.findViewById(R.id.listView);
        this.mListView.setAdapter(this.mListAdapter);
        this.mListView.setOnItemClickListener(this.mItemClickListener);
    
        if (this.mCachedData == null) {
            Log.d("onCreateView", "I make the request");
            this.downloadData();
            ... // After download is finished, you put somewhere:
            this.mCachedData = downloadedData;
        } else {
            Log.d("onCreateView", "Cool, the data is cached");
            this.buildList(this.mCachedData);
        }
    
        return layout;
    }
    
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // You put the content of your list, which is this.mCachedData, in the state
        outState.getParcelableArrayList(BUNDLE_CACHE, this.mCachedData);
    }
    

    我还在我的一些应用程序上使用 web 服务,并且 savedInstanceState 对于在我在片段之间切换视图时不再进行 web 服务调用非常有用。 这种情况适用于片段视图被销毁但片段仍然存在的情况。再次创建视图时,它将使用缓存的数据,而不是从 Web 服务再次下载。


    要将 Parcelable 从 Activity 发送到片段(根据 biovamp 的示例),您可以使用:

    Bundle args = new Bundle();
    args.putParcelable("keyName", parcelableObject);
    fragment.setArguments(args);
    

    在您的片段中,您可以使用它来获取 Parcelable:

    this.getArguments().getParcelable("keyName");
    

    要创建 Parcelable,请参考本教程,例如:http://techdroid.kbeanie.com/2010/06/parcelable-how-to-do-that-in-android.html

    现在,你说:

    然后,当我从列表中单击一个项目时,我想打开一个新活动

    因此,如果您仍想从 ListFragment 创建 DetailsActivity,请使用 Intent 发送数据:

    ListItem item = ... // get your item data
    intent.putExtra("keyItem", item);
    

    并使用以下方法将其放入新创建的 DetailsActivity 中:

    Bundle extras = getIntent().getExtras();
    if (extras !=null) {
        ListItem value = (ListItem) extras.getParcelable("keyItem");
    }
    

    【讨论】:

    • 谢谢米歇尔-F。在最后一个代码中,我得到了 keyName,但我需要整个“Item”对象。保存列表(或地图)的更好地方在哪里,我将使用该 keyName 搜索项目?
    • 我为您的 Parcelable 和 Fragments 案例编辑了我的示例。我调整了关于保存状态的解释。
    • 谢谢米歇尔,这很有帮助。 @FedericoGarcía,您应该将此答案标记为正确答案;)
    【解决方案2】:

    您应该将列表保存在任何持久存储中(并且应用程序不是持久存储)。 SQLite 数据库将最适合您的情况。

    与其创建新的 Activity,不如创建新的 Fragment,将其添加到 Activity,然后使用所需对象作为参数调用一些(自定义)函数(例如loadMyItem())。这样它就不需要 Intent 并且看起来更具可读性(以及今天推荐的方式)

    示例

    主要思想是,当您在SectionsFragment 上执行某些操作(例如单击ListView 的项目)时,它调用它的父Activity,它是Activity 决定应该加载什么以及在哪里加载。在此示例中,Activity 调用 DetailFragment 以在其中加载一些数据。

    你的活动类:

    public class MainActivity extends Activity{
    
        private DetailFragment mDetailFragment;
        private SectionsFragment mSectionsFragment;
    
        /*initialization stuff, it's not interesting now*/
    
        //Section is just custom class for example - it can be whatever you want
        public void setSection(Section section){
            mDetailFragment.loadSection(section);
        }
    }
    

    您的 SectionsFragment:

    public class SectionsFragment extends Fragment{
    
        /*initialization is also skipped*/
    
        /**This method may be called when ListView's item is touched or somwhere else - it's doesn't matter*/
        private void setSection(Section section){
            if(!(getActivity() instanceof MainActivity))
                throw new ClassCastException("This fragment can be created only by MainActivity");
            MainActivity activity = (MainActivity) getActivity();
            activity.setSection(section);
        }
    }
    

    还有你的 DetailFragment:

    public class DetailFragment extends Fragment{
    
        /*initialization*/
    
        public void loadSection(Section section){
            //Here your fragment decides what exactly it should load
            //for this section. Maybe do some networking, or load data from database
            //whatever you want
        }
    }
    

    【讨论】:

    • 感谢 biovamp 我明白了,但是如果我有两个片段(列表和详细信息),我如何将该对象从另一个传递给另一个?
    • @FedericoGarcía 如 developer.android.com 上所述,您应该通过您的活动传递对象。是的,会有班级演员(如(MyActivity) getActivity),但没关系。我很快会在我的回答中提供更多解释
    • @FedericoGarcía 我添加了一个示例
    猜你喜欢
    • 1970-01-01
    • 2012-07-21
    • 2011-10-25
    • 2011-04-11
    • 2021-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多