我已经看到了两种在应用程序中实例化新Fragment的一般做法:
Fragment newFragment = new MyFragment();
和
Fragment newFragment = MyFragment.newInstance();
第二个选项使用静态方法newInstance()
,通常包含以下方法。
public static Fragment newInstance()
{
MyFragment myFragment = new MyFragment();
return myFragment;
}
首先,我认为主要的好处是我可以重载newInstance()方法以在创建Fragment的新实例时提供灵活性 - 但我也可以通过为Fragment创建重载的构造函数来实现这一点。 / p>
我错过了什么吗?
一种方法比另一种方法有什么好处?或者这只是一种好习惯吗?
答案 0 :(得分:1047)
如果Android决定稍后重新创建Fragment,它将调用片段的无参数构造函数。所以重载构造函数不是解决方案。
话虽如此,将内容传递给Fragment以便在Android重新创建片段后可用的方法是将包传递给setArguments
方法。
因此,例如,如果我们想将一个整数传递给片段,我们会使用类似的东西:
public static MyFragment newInstance(int someInt) {
MyFragment myFragment = new MyFragment();
Bundle args = new Bundle();
args.putInt("someInt", someInt);
myFragment.setArguments(args);
return myFragment;
}
稍后在片段onCreate()
中,您可以使用以下方法访问该整数:
getArguments().getInt("someInt", 0);
即使片段以某种方式由Android重新创建,也可以使用此Bundle。
另请注意:setArguments
只能在片段附加到活动之前调用。
这种方法也记录在android开发人员参考:https://developer.android.com/reference/android/app/Fragment.html
中答案 1 :(得分:94)
使用我看到的newInstance()
的唯一好处如下:
您将只有一个地方可以捆绑片段使用的所有参数,并且每次实例化片段时都不必编写以下代码。
Bundle args = new Bundle();
args.putInt("someInt", someInt);
args.putString("someString", someString);
// Put any other arguments
myFragment.setArguments(args);
这是告诉其他类忠实地期望哪些参数忠实工作的好方法(尽管如果片段实例中没有捆绑参数,您应该能够处理案例)。
所以,我的看法是使用静态newInstance()
来实例化片段是一种很好的做法。
答案 2 :(得分:61)
还有另一种方式:
Fragment.instantiate(context, MyFragment.class.getName(), myBundle)
答案 3 :(得分:40)
虽然@yydl给出了一个令人信服的理由,说明为什么newInstance
方法更好:
如果Android决定稍后重新创建片段,则会调用 片段的无参数构造函数。所以超载了 构造函数不是解决方案。
仍然可以使用构造函数。要了解其原因,首先我们需要了解为什么Android会使用上述解决方法。
在使用片段之前,需要一个实例。 Android调用YourFragment()
(无参数构造函数)来构造片段的实例。在这里,您编写的任何重载构造函数都将被忽略,因为Android无法知道要使用哪一个。
在Activity的生命周期中,片段如上所述创建并被Android多次销毁。这意味着如果将数据放入片段对象本身,则一旦片段被销毁,它就会丢失。
要解决此问题,Android会要求您使用Bundle
(调用setArguments()
)来存储数据,然后可以从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的片段类:
/**
* 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将不会为你的片段提供一个 no arg < / em>默认构造函数。因此,这需要您定义一个 no arg 构造函数,这是您可以使用newInstance
工厂方法避免的代码。
编辑:Android不再允许对片段使用重载构造函数。您必须使用newInstance
方法。
答案 4 :(得分:16)
我不同意与yydi answer说:
如果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);
}
即使someInt
已由系统重新创建,也可以让getArguments()
从Fragment
开启static
。这是比static
构造函数更优雅的解决方案。
我认为Fragment
构造函数是无用的,不应该使用。如果将来你想扩展这个static
并为构造函数添加更多功能,他们也会限制你。使用{{1}}构造函数,您无法执行此操作。
<强>更新强>
Android添加了检查,标记所有非默认构造函数并出现错误 由于上述原因,我建议禁用它。
答案 5 :(得分:12)
一些 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"}
答案 6 :(得分:3)
在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系统重新创建你的片段时,如果觉得系统需要更多内存,那么它将通过使用没有参数的构造函数来重新创建你的片段。
您可以在此处找到有关best practice to instantiate fragments with arguments的更多信息。
答案 7 :(得分:2)
由于关于最佳实践的问题,我想补充说,在使用某些REST Web服务时,使用混合方法创建片段通常是个好主意
对于显示用户片段的情况,我们无法传递复杂对象,例如某些用户模型
但我们可以做的是检查onCreate
该用户!= null,如果没有 - 然后将他从数据层中删除,否则 - 使用现有。
通过这种方式,我们既可以获得由userId重新创建的功能,也可以通过Android进行片段重新创建和用户操作的快捷方式,以及通过保持对象本身或仅通过它来创建片段的能力
有点像这样:
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);
}
}
答案 8 :(得分:1)
使用此代码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");
第一个片段中的值必须是静态的
快乐代码
答案 9 :(得分:1)
我最近在这里。但是我刚刚知道的一些东西可能会对您有所帮助。
如果您正在使用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")
fun newInstance(): ChildSampleFragment {
val child = UserProfileFragment.newInstance<ChildSampleFragment>("XYZ")
// Do anything with the current initialized args bundle here
// with child.arguments = ....
return child
}
快乐的编码。
答案 10 :(得分:0)
实例化片段的最佳方法是使用默认的 Fragment.instantiate 方法或创建工厂方法来实例化片段
注意:总是在fragment中创建一个空构造函数,而恢复片段内存会抛出运行时异常。
答案 11 :(得分:0)
您可以像这样使用smth:
val fragment = supportFragmentManager.fragmentFactory.instantiate(classLoader, YourFragment::class.java.name)
因为此answer现在已弃用
答案 12 :(得分:0)
putParceble
并使该接口实现parceble
OnAttach
中初始化侦听器,其中我们有 context[ (context) Listener]。这样在配置更改(例如字体更改)期间,Activity 娱乐侦听器就不会uninitialize
并且我们可以避免空指针异常。
答案 13 :(得分:-1)
public BleMeshTopologyCtrlBlock() {
devPiconet = new ArrayList<BleMeshDevicePiconet>(2);
private void AddMastertoSlave(BleMeshDevice linkMaster,
BleMeshDevice linkSlave, int devRole) {
devPiconet.add(new BleMeshDevicePiconet(devRole, linkSlave));
devPiconet.get(devPiconet.size() -1).getDevSlaves().add(linkMaster);
for (BleMeshDevGatewayTable aRow : getDevPiconet().get(0)
.getGatewaytable()) {
if (aRow.getPicRemoteMaster().equals(aNeighPiconetMaster)
&& aRow.getPicLocalGatewayDev().getDevMiddleware()
.getTopologyCtrlBlock().getCapacity() > 0
&& aRow.getPicRemoteGatewayDev().getDevMiddleware()
.getTopologyCtrlBlock().getCapacity() > 0
没用。它只会带来一团糟。
setArguments()
答案 14 :(得分:-11)
我相信我有更多的解决方案。
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;
}