给Fragment提供初始数据的正确方法是什么?

60

我已经学到,在我的片段中需要一个空构造函数才能避免重新初始化时出现崩溃。我的问题是,当我的片段被初始化时(至少有一些),我使用数据列表。那么有什么好的方法可以使用包含数据列表的方式启动新的片段呢? 我应该在 OnCreate() 中创建一个 getData 方法,从其他来源获取数据,还是有更合适的方法?

向 bundle 中传递数据并不是一个很好的方法,因为我有很多数据。

那么让我们来看一个例子(这样我会更好理解)。

当用户点击按钮时,片段就会启动。我过去常用的方式是这样创建新片段的:

    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    
    fragmentTransaction.replace(R.id.center_container, new DetailFragment(item));
    fragmentTransaction.addToBackStack(DETAIL_TAG);
    
    fragmentTransaction.commit();

然后在我的片段中:

public DetailFragment(EventItem item) {
    mItem = item;
    mPlaces = Database.getContainerPlaces(getActivity()).getValidItems();
}

我无法将所有数据传递给bundle,所以那样行不通。那么我该怎么办?

A:我应该使用空构造函数初始化片段,然后从我的Activity中使用setter直接在片段中设置数据吗?但是,如果用户按下主页键,Android关闭片段,并且用户稍后返回,那么我会丢失数据吗?

B:我应该使用工厂模式初始化片段并调用setRetainInstance(true),为标识数据给片段赋予一个键,然后让片段从第三方来源在onCreateView中获取所需的数据吗?

C:我只需创建一个空构造函数,然后在onCreate()中获取片段所需的数据吗?

需要注意的是,应用程序被锁定在竖屏模式下,因此问题主要涉及在Android关闭并重新启动时维护对象。


为什么不创建一个无界面的Fragment并设置setRetainedInstance(true),在Fragment上放置一个标签,当您再次启动时,可以使用FragmentManager根据标签获取该Fragment。您可以将所有数据放入无界面的Fragment中,只需调用getData即可。是不是很简单! - AZ_
@AZ_ 除了我需要另一个片段来包含视图的意思之外,再加上两个片段只会使代码更难读。 - Warpzit
我认为这会使你的代码更加健壮。只需声明一个内部无界面片段setRetainedInstance(true),你也可以将其用作后台工作线程,这是Google推荐的解决方案。我在developer.android.com上读到过。 - AZ_
处理运行时变更 http://developer.android.com/guide/topics/resources/runtime-changes.html - AZ_
1
@AZ_ 是的,但处理线程和处理数据是两回事。如果我需要处理一些线程,我会同意你的观点,但如果只是原始数据,工厂模式是最好的选择。这也是谷歌根据他们自己的文档所做的:http://developer.android.com/guide/components/fragments.html - Warpzit
没错,你说得对。谢谢 :-) - AZ_
1个回答

74

那么如何使用数据列表来启动新的Fragment呢?

可以使用工厂模式和“参数”Bundle,例如:

package com.commonsware.empublite;

import android.os.Bundle;

public class SimpleContentFragment extends AbstractContentFragment {
  private static final String KEY_FILE="file";

  protected static SimpleContentFragment newInstance(String file) {
    SimpleContentFragment f=new SimpleContentFragment();

    Bundle args=new Bundle();

    args.putString(KEY_FILE, file);
    f.setArguments(args);

    return(f);
  }

  @Override
  String getPage() {
    return(getArguments().getString(KEY_FILE));
  }
}

如果您保留了片段实例,那么只需使用普通的setter将东西放入数据成员即可。 "arguments" Bundle 作为配置更改的一部分而保留,因此对于不保留的实例,这是确保在用户旋转屏幕等情况下保留设置数据的方法。


1
@Warpzit:你的控制器(片段)从模型(数据库、ContentProvider、POJO等)中检索数据并更新视图。我无法确定这是你编辑后问题的B还是C。 - CommonsWare
2
@Warpzit:是的,片段将从托管活动接收其需要了解从模型中获取什么的任何标识信息。其中一些可能在创建时提供。如果存在更新现有片段中该数据的情况,那么setRetainInstance()就会变得非常方便... :-) - CommonsWare
1
setRetainInstance() 适用于 所有 配置更改,不仅限于方向。 - CommonsWare
2
@CommonsWare 我认为这个答案不完整:文件名是否指向一个实际序列化数据列表的文件?工厂模式是否用于检索此列表。在当前代码中,我只看到了 Singleton 的实现。也许答案的建议是保留单个片段实例,然后使用设置器添加其他数据? - Boris Strandjev
1
@BorisStrandjev:“如果不使用setter,如何在保留实例方法中交换数据?”--将其传递到工厂方法(newInstance())中,然后工厂方法可以向实例提供数据。要明确的是,在我之前的回答中,我真正意思是“公共setter”。 “如果您可以控制其类型声明,您对使该对象Parcelable / Serializable有何看法?”--总体上,我尽量避免这样做。如果您有其他问题,请提出一个新的SO问题,而不是扩展一年前的评论链。 - CommonsWare
显示剩余14条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接