我已经看到了在应用程序中实例化一个新Fragment的两个通用实践:

Fragment newFragment = new MyFragment();

and

Fragment newFragment = MyFragment.newInstance();

第二个选项使用静态方法newInstance(),通常包含以下方法。

public static Fragment newInstance() 
{
    MyFragment myFragment = new MyFragment();
    return myFragment;
}

起初,我认为主要的好处是,我可以重载newInstance()方法,以便在创建新的Fragment实例时提供灵活性——但我也可以通过为Fragment创建重载构造函数来实现这一点。

我错过什么了吗?

一种方法相对于另一种方法有什么好处?还是说这只是一个很好的练习?


当前回答

我最近在这里。但我知道的一些事也许能帮到你。

如果您正在使用Java,那么没有什么需要更改的。但是对于Kotlin开发人员来说,下面是一些代码片段,我认为它们可以让你成为一个运行的基础:

父母片段:

inline fun <reified T : SampleFragment> newInstance(text: String): T {
    return T::class.java.newInstance().apply {
        arguments = Bundle().also { it.putString("key_text_arg", text) }
    }
}

正常调用

val f: SampleFragment = SampleFragment.newInstance("ABC")
// or val f = SampleFragment.newInstance<SampleFragment>("ABC")

你可以在子fragment类中扩展父init操作:

fun newInstance(): ChildSampleFragment {
    val child = UserProfileFragment.newInstance<ChildSampleFragment>("XYZ")
    // Do anything with the current initialized args bundle here
    // with child.arguments = ....
    return child
}

快乐的编码。

其他回答

理想情况下,我们不应该在片段构造函数中传递任何东西,片段构造函数应该是空的或默认的。 现在第二个问题是,如果我们想传递接口变量或参数 我们应该使用Bundle来传递数据。 对于Interface,我们可以在bundle中putParceble,并使该接口实现parceble 如果可能的话,我们可以在活动和片段中实现该接口,我们可以在OnAttach中初始化监听器,在那里我们有上下文[(上下文)监听器]。

因此,在配置更改期间(例如字体更改),Activity重新创建侦听器不会去非初始化,我们可以避免空指针异常。

实例化片段的最好方法是使用默认的片段。实例化方法或创建工厂方法来实例化片段 注意:总是在片段中创建一个空构造函数,而在恢复片段内存时会抛出运行时异常。

而@yydl给出了一个令人信服的理由,为什么newInstance方法更好:

如果Android决定稍后重新创建Fragment,它会调用 片段的无参数构造函数。重载 构造函数不是解决方案。

仍然可以使用构造函数。要了解原因,首先我们需要了解为什么Android使用上述解决方案。

在使用片段之前,需要一个实例。Android调用YourFragment()(无参数构造函数)来构造一个片段的实例。这里你写的任何重载构造函数都将被忽略,因为Android不知道该使用哪个构造函数。

在一个Activity的生命周期中,fragment会像上面那样被创建,并被Android多次销毁。这意味着如果您将数据放在片段对象本身中,一旦片段被销毁,这些数据就会丢失。

为了解决问题,android要求你使用一个Bundle存储数据(调用setArguments()),然后可以从YourFragment访问。参数包受到Android的保护,因此保证是持久化的。

设置这个bundle的一种方法是使用静态的newInstance方法:

public static YourFragment newInstance (int data) {
    YourFragment yf = new YourFragment()
    /* See this code gets executed immediately on your object construction */
    Bundle args = new Bundle();
    args.putInt("data", data);
    yf.setArguments(args);
    return yf;
}

然而,构造函数:

public YourFragment(int data) {
    Bundle args = new Bundle();
    args.putInt("data", data);
    setArguments(args);
}

可以做与newInstance方法完全相同的事情。

当然,这会失败,这也是Android希望你使用newInstance方法的原因之一:

public YourFragment(int data) {
    this.data = data; // Don't do this
}

作为进一步的解释,这里是Android的片段类:

/**
 * Supply the construction arguments for this fragment.  This can only
 * be called before the fragment has been attached to its activity; that
 * is, you should call it immediately after constructing the fragment.  The
 * arguments supplied here will be retained across fragment destroy and
 * creation.
 */
public void setArguments(Bundle args) {
    if (mIndex >= 0) {
        throw new IllegalStateException("Fragment already active");
    }
    mArguments = args;
}

注意,Android要求参数只在构造时设置,并保证这些参数将被保留。

编辑:正如@JHH在评论中指出的,如果你提供了一个需要一些参数的自定义构造函数,那么Java不会为你的片段提供一个无参数的默认构造函数。所以这需要你定义一个无参数构造函数,这是你可以用newInstance工厂方法避免的代码。

编辑:Android不再允许对片段使用重载构造函数。您必须使用newInstance方法。

setArguments()没有用。这只会带来混乱。

public class MyFragment extends Fragment {

    public String mTitle;
    public String mInitialTitle;

    public static MyFragment newInstance(String param1) {
        MyFragment f = new MyFragment();
        f.mInitialTitle = param1;
        f.mTitle = param1;
        return f;
    }

    @Override
    public void onSaveInstanceState(Bundle state) {
        state.putString("mInitialTitle", mInitialTitle);
        state.putString("mTitle", mTitle);
        super.onSaveInstanceState(state);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
        if (state != null) {
            mInitialTitle = state.getString("mInitialTitle");
            mTitle = state.getString("mTitle");
        } 
        ...
    }
}

使用kotlin代码创建片段的实例。

在活动中写入

val fragment = YourFragment.newInstance(str = "Hello",list = yourList)

分段写作

fun newInstance(str: String, list: ArrayList<String>): Fragment {
        val fragment = YourFragment()
        fragment.arguments = Bundle().apply {
            putSerializable("KEY_STR", str)
            putSerializable("KEY_LIST", list)
        }
        return fragment
    }

使用相同的片段从bundle中检索数据

val str = arguments?.getString("KEY_STR") as? String
val list = arguments?.getSerializable("KEY_LIST") as? ArrayList<String>