房产网站 设计方案/大数据统计网站
前言
在我们平常开发中经常会用到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;@Overrideprotected 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;}@Overrideprotected 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;}@Overridepublic int describeContents() {return 0;}@Overridepublic 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>(){@Overridepublic FilterTab[] newArray(int size){return new FilterTab[size];}@Overridepublic FilterTab createFromParcel(Parcel in){return new FilterTab(in);}};public FilterTab(Parcel in){name = in.readString();isChecked = in.readByte() != 0; //isChecked == true if byte != 0}
}