使用newInstance()来实例化fragment并传递数据操作
作者:壹只很拽的猫 发布时间:2022-07-11 15:41:57
好问题。答案就是这篇文章的题目所建议的,这是一种合理的设计。在这种情况下,newInstance()方法是一种“静态工厂方法",让我们在初始化和设置一个新的fragment的时候省去调用它的构造函数和额外的setter方法。
为你的Fragment提供静态工厂方法是一种好的做法,因为它封装和抽象了在客户端构造对象所需的步骤。
例如,考虑下面的代码:
public class MyFragment extends Fragment {
/**
* 静态工厂方法需要一个int型的值来初始化fragment的参数,
* 然后返回新的fragment到调用者
*/
public static MyFragment newInstance(int index) {
MyFragment f = new MyFragment();
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
}
不要让客户端去调用默认的构造函数,然后手动地设置fragment的参数。我们直接为它们提供一个静态工厂方法。这样做比调用默认构造方法好,有两个原因:一个是,它方便别人的调用。另一个是,保证了fragment的构建过程不会出错。通过提供一个静态工厂方法,我们避免了自己犯错--我们再也不用担心不小心忘记初始化fragmnet的参数或者没正确设置参数。
总的来说,虽然两者的区别只在于设计,但是他们之间的差别非常大。因为提供静态工厂方法有向上抽象了一个级别,让代码更容易懂。
译者注:
其实提供静态工厂而不是使用默认构造函数或者自己定义一个有参的构造函数还有至关重要一点。fragmnet经常会被销毁重新实例化,Android framework只会调用fragment无参的构造函数。在系统自动实例化fragment的过程中,你没有办法干预。一些需要外部传入的参数来决定的初始化就没有办法完成。使用静态工厂方法,将外部传入的参数可以通过Fragment.setArgument保存在它自己身上,这样我们可以在Fragment.onCreate(...)调用的时候将这些参数取出来。
传递数据
public static LoginFragment newInstance(String param) {
LoginFragment fragment = new LoginFragment();
Bundle args = new Bundle();
args.putString("name", param);
fragment.setArguments(args);
return fragment;
}
在fragment 的onCreatView里获取数据
@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View myView = inflater.inflate(R.layout.xxx, container, false); String args = getArguments().getString("name"); return myView;}
在Activity里
LoginFragment loginFragment= LoginFragment.newInstance(想要传递的参数);
SignUpFragment signUpFragment= SignUpFragment.newInstance(想要传递的参数);
List<Fragment> allFragment = new ArrayList<Fragment>();
allFragment.add(loginFragment);
allFragment.add(signUpFragment);
补充知识:正确使用Fragment之创建/传参——newInstance方法(native)
说来忏愧,近来越发觉得写不出可分享的东西,更糟糕的是,甚至觉得可记录的东西都不多。
这实在是一个非常糟的信号——说明我开始逐渐把自己放在安全边际内了。
人若总是将自己畏缩在安全边际之内,不去做一些阵痛的改变,埋下的会是病来如山倒般的灾难种子。
好在,好在我还在不断的学习,只是但前处于一种较混沌的状态,需要踏出去更多一步。
今天来说一个简单的话题,找回一些状态。
关于Fragment,相信大家已经熟之不能再熟了。然而,
使用频率如此之高的Fragment,你的使用姿势,真的正确吗?
先对比一下两种使用姿势:
1.姿势A:
MyFragment mFragment = new MyFragment();
Bundle bundle = new Bundle();
bundle.putString("arg1", "a");
bundle.putString("arg2", "b");
bundle.putString("arg3", "c");
mFragment.setArguments(bundle);
getSupportFragmentManager().beginTransaction().replace(R.id.frame, mFragment).commit();
2.姿势B:
MyFragment mFragment = MyFragment.newInstance("a", "b","c");
getSupportFragmentManager().beginTransaction().replace(R.id.frame, mFragment).commit();
有没有,有没有觉得第二种姿势特别爽。
接来下进入今天的正题,关于Fragment.newInstance()这个方法。
我先声明,其实第一种姿势没什么问题,(引用斯坦福白胡子老头一句话)”这只是代码风格的问题,但我不建议这么做。”
使用Android Studio新建一个Fragment就一切明了了:
我们看到,Studio默认帮我们创建的Fragment中,有这样一段代码:
// TODO: Rename and change types and number of parameters
public static BlankFragment newInstance(String param1, String param2) {
BlankFragment fragment = new BlankFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
一个静态方法,返回我们创建的Fragment类本身,显而易见的是,这个方法帮我们做了姿势A中我们手写的方法。
再来关注看我们较少Override的方法onCreate(这里默认直接帮我们Override了)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
到这里,我们先捋一捋。姿势B的机理在于,通过传递参数给Fragment.newInstance()方法,它会创建一个该Fragment类,并通过创建Bundle把我们的参数代入。然后在onCreate()生命周期中,把参数拿回出来。(为什么这么做?是本文后半部分传参讨论的内容,先跳过),之后的事情大家都是熟手了,把参数拿来用就好。
为什么谷歌默认要使用这样一个工厂方式创建我们的Fragment呢?
既然newInstance()是父类Fragment的方法,我们跟进去一看究竟:
//可以看到这是一个native方法
public native T newInstance() throws InstantiationException, IllegalAccessException;
题外话:关于native方法: native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
就感觉线索断了一样,这是要往下去读C/C++啊,抛开底层机理不知,不说,姑且猜测为,二者完全是一样的方式,只是姿势B封装了一点内容,让Fragment的宿主Activity更加整洁一些,仅此而已。
既然如此,我们转为本文的下半部分,关于传参。
想过吗?Fragment作为java类
为什么传参需要用Fragment.setArguments(bundle)这样的方式,
而不通过构造函数直接传递new Fragment(arg1,arg2);
实践出真知,其实在大多数时候,这两种方法传递参数都是没有问题的。
但是,但是当某些情景发生,一切就不一样了。(比如竖屏切换横屏时),切换到横屏时,构造方法传递的参数就找不到了。
原因很简单,因为Fragment是有自己封装的生命周期的,这一点和Activity类似,Activity传参也不是用构造方法的方式。
但是究竟生命周期对构造方法传递参数有什么影响呢?
源码中一探究竟:
在Fragment中,是通过Bundle来保存参数的,它的私有声明在此:
Bundle mArguments;
顺着这个声明的命名mArguments找下去,发现其实相关的主要方法并不多:
public FragmentState(Fragment frag) {
...
mArguments = frag.mArguments;
...
}
public void setArguments(Bundle args) {
if (mIndex >= 0) {
throw new IllegalStateException("Fragment already active");
}
mArguments = args;
}
final public Bundle getArguments() {
return mArguments;
}
这三个比较简单,就不说了
public Fragment instantiate(FragmentHostCallback host, Fragment parent,
FragmentManagerNonConfig childNonConfig) {
if (mInstance == null) {
final Context context = host.getContext();
if (mArguments != null) {
mArguments.setClassLoader(context.getClassLoader());
}
mInstance = Fragment.instantiate(context, mClassName, mArguments);
if (mSavedFragmentState != null) {
mSavedFragmentState.setClassLoader(context.getClassLoader());
mInstance.mSavedFragmentState = mSavedFragmentState;
}
mInstance.setIndex(mIndex, parent);
mInstance.mFromLayout = mFromLayout;
mInstance.mRestored = true;
mInstance.mFragmentId = mFragmentId;
mInstance.mContainerId = mContainerId;
mInstance.mTag = mTag;
mInstance.mRetainInstance = mRetainInstance;
mInstance.mDetached = mDetached;
mInstance.mHidden = mHidden;
mInstance.mFragmentManager = host.mFragmentManager;
if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
"Instantiated fragment " + mInstance);
}
mInstance.mChildNonConfig = childNonConfig;
return mInstance;
}
在instantiate()实例化过程中,可以看到
if (mArguments != null) {
mArguments.setClassLoader(context.getClassLoader());
}
也就是说,如果我们调用时使用setArguments()传递了Bundle,它会被保存在mArguments 这个私有声明中。
而如果是通过构造函数传递的参数,那很不幸,Fragment重建过程中,并没有持有相应参数的属性或方法,自然,你通过构造函数传递的参数就丢失了。
其实目前大家单纯无参new Fragment()的方式并没有错,只是可以让Activity更优雅的调用Fragment.newInstance(),
而如果涉及到传递参数,万不可通过构造函数传递,会丢失。
知其然,知其所以然
总结,Fragment.newInstance() ,别无其他,只是事关风格(代码”整”“洁”之道),建议大家以后均使用谷歌推荐的该方法
来源:https://blog.csdn.net/tiramisu_ljh/article/details/69808711
猜你喜欢
- 环境搭建spring boot的简介以往我们开发时用到spring总是避免不了繁琐的配置,例如我们要配置一个数据库连接,可能需要以下几步:1
- 本文实例为大家分享了java实现计算器功能具体代码,供大家参考,具体内容如下效果图组成结构从结构上来说,一个简单的图形界面,需要由界面组件、
- 程序如下:View Code /* * Hanoi塔游戏 问题描述: * 汉诺塔:汉诺塔(又称河内塔)问
- 假定你已经了解了运行时的数据区域和常用的垃圾回收算法,也了解了Hotspot支持的垃圾回收器。一、cpu占用过高cpu占用过高要分情况讨论,
- Navigator 的 push 和 pop方法Navigator 导航器的 push 和 pop 方法可以携带参数在页面间传递,其他变形的
- 使用AspectJ实现AOP注解方式XML方式AspectJ简介AspectJ是一个基于Java语言的AOP框架Spring2.0以后新增了
- 本文实例讲述了C#中动态数组用法。分享给大家供大家参考。具体分析如下:ArrayList是一种动态数组,其容量可随着我们的需要自动进行扩充.
- 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了。近年来,随着HTML5的诞生,WebSocket协议被提出,它实
- Java语言是一种半编译半解释的语言。Java的用户程
- 引言在Broker中,事务消息的初始化是通过BrokerController.initialTransaction()方法执行的。priva
- 星期天小哼和小哈约在一起玩桌游,他们正在玩一个非常古怪的扑克游戏——“小猫钓鱼”。游戏的规则是这样的:将一副扑克牌平均分成两份,每人拿一份。
- 1、什么是GradleGradle是一种结合了Ant和Maven两者优势的下一代构建工具,既有Ant构建灵活性的优点,也保留Maven约定优
- package org.load.u;import java.io.File;import java.util.LinkedHashMap;
- 门面模式又叫外观模式(Facade Pattern),主要用于隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。我们知道电视剧
- 1. 引入当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象, 只有通过new关键字才会产生出对象,这时系统才会
- 前言前面一篇我们介绍了使用 shared_preferences实现简单的键值对存储,然而我们还会面临更为复杂的本地存储。比如资讯类 App
- springboot项目启动的时候参数无效今天启动一个springboot项目发现启动的时候输入的参数都是不能生效,但是yaml文件的配置却
- 使用foreach循环的坑我们首先看一段MyBatis中使用foreach循环的sql:SELECT * FROM table where
- 有的时候,我们需要对一堆数据进行统计分析后生成HTML或Excel格式报表。本来这并不是一件很难的事,但确是件比较麻烦的事情。最令人头痛的是
- 本期文章源码:GitHub一文彻底搞懂《并查集》!概念并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。