您的位置:首页 > 其它

如何向一个Fragment传递参数---setArguments方法的介绍

2017-06-01 19:02 435 查看
转载自 http://blog.csdn.net/small_lee/article/details/50553881

在我们平常开发中经常会用到Fragment,当我们使用Fragment时一般是通过new Fragment的构造方法来实现,如果我问你怎么向一个Fragment传递参数,你是不是会首先想到通过构造方法,当面试被问到这个问题的时候我也是这么想的,后来发现自己错了,现在给大家讲一下究竟该怎么做。

首先我们看构造方法这种方式为什么不行,根据Android文档说明,当一个fragment重新创建的时候,系统会再次调用 Fragment中的默认构造函数。 注意这里:是 默认构造函数。 

这句话更直白的意思就是:当你小心翼翼的创建了一个带有重要参数的Fragment的之后,一旦由于什么原因(横竖屏切换)导致你的Fragment重新创建。——-很遗憾的告诉你,你之前传递的参数都不见了,因为recreate你的Fragment的时候,调用的是默认构造函数。

首先我们通过构造函数来传递参数,代码如下
public class MainActivity extends FragmentActivity {

private FragmentManager manager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
manager = getSupportFragmentManager();
/*
* 这里为什么要进行空判断,因为在屏幕旋转的时候,系统会执行onSaveInstanceState
* 方法去保存当前activity的状态,然后activity会重建,执行onCreate方法,如果我们不判断
* savedInstanceState是否为空,那么每次就会执行下面的commit操作,向Fragmnet传递参数,
* 这样参数的却会保留下来,但是我们不应该每次都去传递参数。当进行了空判断时,当Activity重建
* 的时候,会调用Fragment的默认构造函数,所以我们传递过去的参数不能保留了。
*/
if(savedInstanceState == null){
manager.beginTransaction().replace(R.id.fl_main, new FragmentOne("params")).commit();
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
System.out.println("=========savedInstanceState ");
super.onSaveInstanceState(outState);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class FragmentOne extends Fragment {

private TextView textView;
private String params = "default";
public FragmentOne(){
System.out.println("===========default constructor");
}
/**
* 通过构造方法接收传递过来的参数
* @param content
*/
public FragmentOne(String content){
params = content;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container,false);
textView = (TextView) view.findViewById(R.id.textview);
textView.setText(params);
return view;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23



可以看到此时传递过来的参数已经不见了,说明通过构造方法传递参数是不行的。

我们看看控制台的打印



注意: 
这里我们必须写出默认的构造函数,因为Fragment重建的时候,会调用默认的构造函数,也就是空参数的构造函数,如果我们只是给出了带参数的构造函数,系统是不会为我们创建空参数的构造函数的,如果你不写,在Fragment重建的时候就会发生下面的错误。



接下来看看官方推荐的setArguments方法:
public class MainActivity extends FragmentActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.fl_main, FragmentOne.newInstance("params"))
.commit();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class FragmentOne extends Fragment{
private TextView textView;
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_one, null);
textView = (TextView) view.findViewById(R.id.textview);
if(getArguments()!=null){
//取出保存的值
textView.setText(getArguments().getString("name"));
}
return view;
}
public static  FragmentOne newInstance(String text){
FragmentOne fragmentOne = new FragmentOne();
Bundle bundle = new Bundle();
bundle.putString("name", text);
//fragment保存参数,传入一个Bundle对象
fragmentOne.setArguments(bundle);
return fragmentOne;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21



可以看到,屏幕旋转以后参数也保留下来了。

接下来我们通过源码看看Bundle这个参数到底如何保留下来的, 

点进去Fragment的setArguments方法:
public void setArguments(Bundle args) {
if (mIndex >= 0) {
throw new IllegalStateException("Fragment already active");
}
mArguments = args;
}
1
2
3
4
5
6

首先将当前的bundle对象赋值给一个全局的mArguments对象,然后我们看这个mAruments对象在哪里用到了
public Fragment instantiate(Activity activity, Fragment parent) {
if (mInstance != null) {
return mInstance;
}

if (mArguments != null) {
mArguments.setClassLoader(activity.getClassLoader());
}
//重点在这里
mInstance = Fragment.instantiate(activity, mClassName, mArguments);

if (mSavedFragmentState != null) {
mSavedFragmentState.setClassLoader(activity.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.mFragmentManager = activity.mFragments;
if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
"Instantiated fragment " + mInstance);

return mInstance;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

然后我们进入Fragment的instantiate方法
public static Fragment instantiate(Context context, String fname, Bundle args) {
try {
Class<?> clazz = sClassMap.get(fname);
if (clazz == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = context.getClassLoader().loadClass(fname);
sClassMap.put(fname, clazz);
}
Fragment f = (Fragment)clazz.newInstance();
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.mArguments = args;
}
return f;
} catch (ClassNotFoundException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (java.lang.InstantiationException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (IllegalAccessException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

这里通过无參反射构造了一个新的Fragment对象,然后把我们保存的Bundle对象传递给了新的Fragment对象的mAruments成员变量。通过上面的分析,我们就知道了为什么setArguments方法能够保留参数了。

注意: 

setArguments方法的调用必须要在Fragment与Activity关联之前。 

这句话可以这样理解,setArgument方法的使用必须要在FragmentTransaction 的commit之前使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: