安卓项目实战之:关于Fragment构造方法传参问题的解决方案以及两种序列化方案的对比和Parceable形式下boolean类型字段的封装

版权声明:转载必须注明本文转自郭子轩的博客 https://blog.csdn.net/gpf1320253667/article/details/86580179

前言

在我们平常开发中经常会用到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
    }
}

猜你喜欢

转载自blog.csdn.net/gpf1320253667/article/details/86580179
今日推荐