我已经看到了两种在应用程序中实例化新Fragment的常规做法:

Fragment newFragment = new MyFragment();

Fragment newFragment = MyFragment.newInstance();

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

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

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

我错过了什么?

一种方法比另一种方法有什么好处? 还是只是好的做法?


#1楼

还有另一种方法:

Fragment.instantiate(context, MyFragment.class.getName(), myBundle)

#2楼

不同意 yydi的回答

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

我认为这是一个解决方案,也是一个很好的解决方案,这正是Java核心语言开发它的原因。

的确,Android系统可以销毁并重新创建您的Fragment 。 因此,您可以执行以下操作:

public MyFragment() {
//  An empty constructor for Android System to use, otherwise exception may occur.
}

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

即使Fragment是由系统重新创建的,它也允许您someIntgetArguments()获取getArguments() 。 这是比static构造函数更好的解决方案。

我认为static构造函数是无用的,不应使用。 如果将来您想扩展此Fragment并向构造函数添加更多功能,它们也会限制您。 使用static构造函数,您将无法执行此操作。

更新:

Android添加了检查,将所有非默认构造函数标记为错误。
由于上述原因,我建议禁用它。


#3楼

我相信我对此有一个非常简单的解决方案。

public class MyFragment extends Fragment{

   private String mTitle;
   private List<MyObject> mObjects;

   public static MyFragment newInstance(String title, List<MyObject> objects)
   MyFragment myFrag = new MyFragment();
   myFrag.mTitle = title;
   myFrag.mObjects = objects;
   return myFrag;
   }

#4楼

由于存在有关最佳实践的问题,我想补充一点,在使用某些REST Web服务时,通常最好使用混合方法来创建片段。

对于显示用户片段的情况,我们不能传递复杂的对象,例如某些用户模型

但是我们可以做的是检入onCreate该用户!= null,如果没有,则从数据层将其带入,否则使用现有的。

这样一来,我们既获得了通过Android重新创建片段的情况下通过userId进行重新创建的能力,又获得了对用户操作的敏锐度,以及通过持有对象本身或仅保留其ID来创建片段的能力

像这样:

public class UserFragment extends Fragment {
    public final static String USER_ID="user_id";
    private User user;
    private long userId;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        userId = getArguments().getLong(USER_ID);
        if(user==null){
            //
            // Recreating here user from user id(i.e requesting from your data model,
            // which could be services, direct request to rest, or data layer sitting
            // on application model
            //
             user = bringUser();
        }
    }

    public static UserFragment newInstance(User user, long user_id){
        UserFragment userFragment = new UserFragment();
        Bundle args = new Bundle();
        args.putLong(USER_ID,user_id);
        if(user!=null){
            userFragment.user=user;
        }
        userFragment.setArguments(args);
        return userFragment;

    }

    public static UserFragment newInstance(long user_id){
        return newInstance(null,user_id);
    }

    public static UserFragment newInstance(User user){
        return newInstance(user,user.id);
    }
}

#5楼

尽管@yydl给出了为什么newInstance方法更好的令人信服的理由:

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

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

在使用片段之前,需要一个实例。 Android调用YourFragment()无参数构造函数)来构造片段的实例。 在这里,您编写的所有重载构造函数都将被忽略,因为Android无法确定要使用哪个构造函数。

在Activity的生存期内,片段会按上述方式创建并被Android多次破坏。 这意味着,如果将数据放在片段对象本身中,则一旦片段被破坏,数据将丢失。

要解决此问题,android会要求您使用Bundle (调用setArguments() )存储数据,然后可以从YourFragment访问该YourFragment 。 参数bundle受Android保护,因此可以保证是持久的

设置此捆绑包的一种方法是使用静态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的Fragment类:

/**
 * 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将不会为您的片段提供无arg默认构造函数。 因此,这将需要您定义一个no arg构造函数,这是可以使用newInstance factory方法避免的代码。

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


#6楼

实例化片段的最佳方法是使用默认的Fragment.instantiate方法或创建工厂方法来实例化片段
注意:始终在片段中创建一个空的构造函数,而还原片段存储器将抛出运行时异常。


#7楼

在Android中使用参数实例化片段的最佳做法是在片段中使用静态工厂方法。

public static MyFragment newInstance(String name, int age) {
    Bundle bundle = new Bundle();
    bundle.putString("name", name);
    bundle.putInt("age", age);

    MyFragment fragment = new MyFragment();
    fragment.setArguments(bundle);

    return fragment;
}

您应该避免使用片段实例设置字段。 因为每当android系统重新创建您的片段时,如果感觉到系统需要更多的内存,它将比不使用不带参数的构造函数来重新创建您的片段。

您可以在此处找到有关最佳实践的更多信息, 以实例化带有参数的片段


#8楼

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

#9楼

一些kotlin代码:

companion object {
    fun newInstance(first: String, second: String) : SampleFragment {
        return SampleFragment().apply {
            arguments = Bundle().apply {
                putString("firstString", first)
                putString("secondString", second)
            }
        }
    }
}

您可以通过以下方式获取参数:

val first: String by lazy { arguments?.getString("firstString") ?: "default"}
val second: String by lazy { arguments?.getString("secondString") ?: "default"}

#10楼

使用此代码100%修复您的问题

firstFragment中输入此代码

public static yourNameParentFragment newInstance() {

    Bundle args = new Bundle();
    args.putBoolean("yourKey",yourValue);
    YourFragment fragment = new YourFragment();
    fragment.setArguments(args);
    return fragment;
}

此样本发送布尔数据

和在SecendFragment

yourNameParentFragment name =yourNameParentFragment.newInstance();
   Bundle bundle;
   bundle=sellDiamondFragments2.getArguments();
  boolean a= bundle.getBoolean("yourKey");

第一个片段中的必须值是静态的

快乐的代码


#11楼

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

话虽如此,将内容传递给Fragment以便在Android重新创建Fragment后可用的方法是将包传递给setArguments方法。

因此,例如,如果我们想将整数传递给片段,我们将使用类似以下内容:

public static MyFragment newInstance(int someInt) {
    MyFragment myFragment = new MyFragment();

    Bundle args = new Bundle();
    args.putInt("someInt", someInt);
    myFragment.setArguments(args);

    return myFragment;
}

然后在Fragment onCreate()您可以使用以下方式访问该整数:

getArguments().getInt("someInt", 0);

即使Fragment是由Android重新创建的,此Bundle仍然可用。

另请注意:只能在将Fragment附加到Activity之前调用setArguments

android开发人员参考中也记录了这种方法: https : //developer.android.com/reference/android/app/Fragment.html


#12楼

我看到的使用newInstance()的唯一好处是:

  1. 您将有一个地方可以捆绑该片段使用的所有参数,并且不必在每次实例化一个片段时都在下面编写代码。
Bundle args = new Bundle(); args.putInt("someInt", someInt); args.putString("someString", someString); // Put any other arguments myFragment.setArguments(args);
  1. 这是一种告诉其他类它希望忠实工作的参数的好方法(尽管如果片段实例中没有捆绑参数,则您应该能够处理各种情况)。

因此,我的看法是,使用静态newInstance()实例化片段是一种好习惯。