我已经看到了两种在应用程序中实例化新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
是由系统重新创建的,它也允许您someInt
从getArguments()
获取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()
的唯一好处是:
您将有一个地方可以捆绑该片段使用的所有参数,并且不必在每次实例化一个片段时都在下面编写代码。
Bundle args = new Bundle(); args.putInt("someInt", someInt); args.putString("someString", someString); // Put any other arguments myFragment.setArguments(args);
这是一种告诉其他类它希望忠实工作的参数的好方法(尽管如果片段实例中没有捆绑参数,则您应该能够处理各种情况)。
因此,我的看法是,使用静态newInstance()
实例化片段是一种好习惯。