前言
在我们平常开发中经常会用到Fragment,当我们使用Fragment时一般是通过new Fragment的构造方法来实现,并且在实际的项目开发中,我们经常会用到TabLayout+ViewPager+Fragment的方式来实现tab切换效果,此时所有tab对应的fragment有可能布局都是一致的,唯一的区别就是数据源不同,这时感觉就没有必要定义那么多的Fragment了,我们只需要定义一个Fragment,然后在给ViewPager设置适配器时,在创建Fragment的同时将数据源也设置进去,类似如下代码:
mFragments.add(new MyFragment(list1));
mFragments.add(new MyFragment(list2));
mFragments.add(new MyFragment(list3));
可能面对需要传值给Fragment的需求,大多数开发者都会想到和上面一样通过构造方法传值的方案,但是系统并不推荐我们这样做,即使我们在fragmnet中定义了对应的构造方法,但是依然还是会有红线的警告,为什么会这样呢?下面我们详细来讲。
不推荐使用构造方法传参的形式
根据Android文档说明,当一个fragment重新创建的时候,系统会再次调用 Fragment中的默认构造函数,也就是空参数的构造函数,如果我们只是给出了带参数的构造函数,系统是不会为我们创建空参数的构造函数的,如果你不写,在Fragment重建的时候就会发生下面的错误:
那在Fragment构造方法传值的时候,如果我们在Fragment中同时定义了有参和无参的构造方法是不是就可以了呢?
可以是可以,但是并不推荐这么做,因为可能会导致参数数据丢失:
当我们创建了一个带有重要参数的Fragment的之后,一旦由于什么原因(横竖屏切换)导致你的Fragment重新创建。——-很遗憾的告诉你,你之前传递的参数都不见了,因为recreate你的Fragment的时候,调用的是默认构造函数。
官方推荐的setArguments方法来实现传值
1,在Fragment中定义newInstance方法,该方法的参数类型就是我们要传递的参数类型,此处传递的是一个List对象集合,注意使用Bundle传递对象的时候,必须要求该对象实现序列化接口Serializable或者Parceable,
Java中使用的是Serializable,而谷歌在Android使用了自定义的Parcelable。
两种序列化方式的区别:
1.在使用内存的时候,Parcelable比Serializable性能高,推荐使用Parcelable类;
2.Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC;
3.Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下,这种情况建议使用Serializable。
推荐Android开发中应该尽可能的使用Parcleable,效率相对比较高,但是实现起来也相对繁琐一些,关于实现Parcleable接口进行序列化的方法在文章结尾通过一个示例来说明。
public class FilterTabs extends BaseFragment {
private List<FilterTab> all;
@Override
protected int getContentViewId() {
return R.layout.fragment_filter_tab;
}
public static FilterTabs newInstance(ArrayList<FilterTab> list){
FilterTabs ft = new FilterTabs();
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("list",list);
ft.setArguments(bundle);
return ft;
}
@Override
protected void initView() {
all = getArguments().getParcelableArrayList("list");
}
}
然后在传值时就可以这样写:
mFragments.add(FilterTabs.newInstance(new FilterTab().getAll()));
mFragments.add(FilterTabs.newInstance(new FilterTab().getJC()));
mFragments.add(FilterTabs.newInstance(new FilterTab().getZC()));
上面的new FilterTab().getZC()等方法是获取数据源的测试方法,返回结果为对象集合。
这样即使屏幕旋转,fragment重新被创建,参数信息也会被保留下来。
实现Parcleable接口来进行对象序列化操作
public class FilterTab implements Parcelable {
private String name;
private boolean isChecked;
public FilterTab() {
}
public FilterTab(String name) {
this.name = name;
}
public FilterTab(String name, boolean isChecked) {
this.name = name;
this.isChecked = isChecked;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean checked) {
isChecked = checked;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// 序列化过程:必须按成员变量声明的顺序进行封装
dest.writeString(name);
// boolean类型的字段在封装时可借助Byte来实现
dest.writeByte((byte) (isChecked ? 1 : 0)); //if isChecked == true, byte == 1
}
// 反序列过程:必须实现Parcelable.Creator接口,并且对象名必须为CREATOR
// 读取Parcel里面数据时必须按照成员变量声明的顺序,Parcel数据来源上面writeToParcel方法,读出来的数据供逻辑层使用
public static final Parcelable.Creator<FilterTab> CREATOR = new Creator<FilterTab>()
{
@Override
public FilterTab[] newArray(int size)
{
return new FilterTab[size];
}
@Override
public FilterTab createFromParcel(Parcel in)
{
return new FilterTab(in);
}
};
public FilterTab(Parcel in)
{
name = in.readString();
isChecked = in.readByte() != 0; //isChecked == true if byte != 0
}
}