使用newInstance()来实例化fragment并传递数据操作

好问题。答案就是这篇文章的题目所建议的,这是一种合理的设计。在这种情况下,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() ,别无其他,只是事关风格(代码”整”“洁”之道),建议大家以后均使用谷歌推荐的该方法

以上这篇使用newInstance()来实例化fragment并传递数据操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 完美解决Android App启动页有白屏闪过的问题

    应用启动的时候有短暂的白屏,如图: 可以通过设置theme的方式来解决 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item

  • IDEA打包jar-解决找不到或无法加载主类 main的问题

    学习大佬们开发安全小工具,打包jar解决错误: 找不到或无法加载主类 main 1 Maven方式 遇到报错"找不到或无法加载主类 main" 解决方案 一定加入<build> <plugins>中的插件,这里需要注意的是 <mainClass>Main</mainClass>,这里填写的路径为/src/main/java下开始写的 <?xml version="1.0" encoding="UTF-8

  • Android 将本地资源图片转换成Drawable,进行设置大小操作

    前言: 因为项目中显示图片是用Picasso,设置placeholder和error图片的时候发现,本地图片的大小无法满足我的需求,需要先对图片大小改变再显示. Picasso的placeholder和error的参数也只有int resId和Drawable drawable 于是打算将改变过大小的Drawable传进入显示,咦,效果很满意! 整个过程的思路: 将本地图片(R.drawable.image)变成Drawable对象 将Drawable对象转换成Bitmap对象 将Bitmap对

  • AndroidStudio替换项目图标ic_launcher操作

    1.打开项目主界面,任意打开一个类文件,如MainActivity.java,不要打开布局文件的disign界面 2.点击File-->New-->Image Asset,如图: 3.选择要替换的图标 4.选好之后点击ok 5.点击Next 6.点击finish 7.另外,如果你重新运行之后图标没有变化,不用着急,现在智能手机基本上都是桌面和应用同时控制着图标的显示,两者一起改,桌面图标才会变化,而桌面图标是有缓存的,应用改了之后,桌面依然控制着显示原来的图标,清除桌面数据再试试,基本上就能看

  • 使用newInstance()来实例化fragment并传递数据操作

    好问题.答案就是这篇文章的题目所建议的,这是一种合理的设计.在这种情况下,newInstance()方法是一种"静态工厂方法",让我们在初始化和设置一个新的fragment的时候省去调用它的构造函数和额外的setter方法. 为你的Fragment提供静态工厂方法是一种好的做法,因为它封装和抽象了在客户端构造对象所需的步骤. 例如,考虑下面的代码: public class MyFragment extends Fragment { /** * 静态工厂方法需要一个int型的值来初始化

  • Android 两个Fragment之间传递数据实例详解

    Android 两个Fragment之间如何传递数据 FragmentA启动FragmentB,做一些选择操作后,返回FragmentA,需要把FragmentB里面选择的数据传回来.有什么办法? Fragment之间不能直接通信,必须通过Activity来完成,具体步骤. 1. 在FragmentA中定义通信接口,通过该接口向Activity发送数据. public class FragmentA extends Fragment { private onButtonPressListener

  • Android中Activity和Fragment传递数据的两种方式

    1.第一种方式,也是最常用的方式,就是使用Bundle来传递参数 MyFragment myFragment = new MyFragment(); Bundle bundle = new Bundle(); bundle.putString("DATA",values);//这里的values就是我们要传的值 myFragment.setArguments(bundle); 然后在Fragment中的onCreatView方法中,通过getArgments()方法,获取到bundle

  • vue组件Prop传递数据的实现示例

    组件实例的作用域是孤立的.这意味着不能(也不应该)在子组件的模板内直接引用父组件的数据.要让子组件使用父组件的数据,我们需要通过子组件的props选项. prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来.这是为了防止子组件无意修改了父组件的状态. 每次父组件更新时,子组件的所有 prop 都会更新为最新值.这意味着你不应该在子组件内部改变 prop. 1.Prop静态传递数据 <!DOCTYPE html> <html> <head lang=&q

  • 关于Ajax中通过response在后台传递数据问题

    这是js代码: var System = { getHttpRequest: function(url, callback, options) { if (url.length < 0) return; var option = { url: url, type: "get", dataType: "json", cache: false, timeout: 30000, beforeSend: function(XHR) { }, complete: fun

  • 探讨.get .post .ajax ztree 还有后台servlet传递数据的相关知识

    servlet给前台传递data串 用的方法是 PrintWriter out = response.getWriter(); // response.sendRedirect("test.jsp"); String s = "[{'id':'1', 'pId':'0', 'name':'test1'},{'id':'11', 'pId':'1', 'name':'test11'}, {'id':'12', 'pId':'1', 'name':'test12'}, {'id'

  • vue中各组件之间传递数据的方法示例

    前言 本文主要给大家介绍了关于vue组件之间传递数据的相关资料,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 作用域 在vue中,组件实例的作用域是孤立的,父组件模板的内容在父组件作用域内编译:子组件模板的内容在子组件作用域内编译.这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据. 下面几种方法可以实现组件之间数据的传递. 一.通过prop传递数据 1)在子组件中,使用prop属性,显示的表明,它所需要的数据. 2)在父组件中,需要引用子组件的地方,传入数据.

  • vue项目中做编辑功能传递数据时遇到问题的解决方法

    在项目中完成编辑功能时,遇到了这样一个问题:编辑的功能使用的是一个子组件作为弹出框,如图 这里涉及到从父组件向子组件传递数据的问题,这个项目使用的是v1.0,问题是当点击了编辑以后,弹出的子组件中没有获取到父组件传过来的数据,检查后没有发现代码错误,最后解决的方法就是在父组件中,把传递的数据中的每一项都初始化了一遍就好了 creatIssue (type,list,id){ this.modelIssue=true; this.modeltype=type; if(type=='creat'){

  • vuejs动态组件给子组件传递数据的方法详解

    通过子组件定义时候的props可以支持父组件给子组件传递数据,这些定义的props在子组件的标签中使用绑定属性即可,但是如果使用的是<component>动态组件,这个时候就没有显式的子组件标签,要给子组件传递数据需要在<component> 中进行绑定 <div class="app" id="deviceready"> <component :is="currentView" :user_name.s

  • vue.js组件之间传递数据的方法

    前言 组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用.如何传递数据也成了组件的重要知识点之一. 组件 组件与组件之间,还存在着不同的关系.父子关系与兄弟关系(不是父子的都暂称为兄弟吧). 父子组件 父子关系即是组件 A 在它的模板中使用了组件 B,那么组件 A 就是父组件,组件 B 就是子组件. // 注册一个子组件 Vue.component('child', { data: function(){ text: '我是fathe

随机推荐