Fragment与FragmentPagerAdapter的使用

来源:互联网 发布:java迭代器怎么写 编辑:程序博客网 时间:2024/05/16 17:07

Fragment与FragmentPagerAdapter的使用

我也是一名刚刚参加工作四个月的小程序猿,写博客只是为了分享一下博主在工作中遇到的一些困难以及难点,如果有什么说错了或者不足的地方,欢迎前辈们指出。


博主是做盲人应用的,因为ListView对盲人比较不和谐把。。所以公司一般要程序猿用TabHost内放ViewPager的方式来实现需要的效果。前两天公司要求做一个咨询2.0的项目,布局风格是外层TabHost,下面有三个Tab,每个Tab对应的是一个FragmentActivity。博主在写的过程中也遇到了一些困难,现在拿出来跟大家讨论交流下,下面进入正题。

说到Fragment与ViewPager的联合使用就必须得学会联系这两者的Adapter

Fragment的Adapter有两种。一种是FragmentPagerAdapter,另一种是FragmentStatePagerAdapter。从Google官网我们能了解到FragmentPagerAdapter是PagerAdapter中的其中一种实现。它将每一个页面表示为一个 Fragment,并且每一个Fragment都将会保存到fragment manager当中。而且,当用户没可能再次回到页面的时候,fragment manager才会将这个Fragment销毁。那么这样的话,问题就出来了。FragmentPagerAdapter只适合静态的Fragment,因为当该界面不可见时view hierarchy将会被销毁。这样的话会应用程序占用太多资源,所以当页面数量较多时,推荐使用FragmentStatePagerAdapter。

看完Adapter的说明之后,再来说一下博主在工作之中遇到的问题,博主做的是一个资源APP,在另一个界面点击添加后,跳回前一界面时,要同步显示用户订阅的资料。说到这,相信大家也都清楚问题出在哪里了。

问题出在内容刷新这里。首先我们来总结一下问题发生的原因:从官网的文档我们可以得知,FragmentPagerAdapter是会对Fragment做出缓存的。当我们从第二个界面跳回第一个界面时,Activity会执行onResume的方法,而此时我们在Activity的onResume方法中重新加载Fragment并对Adapter进行刷新时,并不会刷新Fragment,而是会去调用FragmentManager中保存的Fragment。

知道问题发生的原因了之后博主在网上看了很多博客,也自己做了一些试验,最后得出了两种可行的方法。

方法1:我们在Adapter中写一个设置Adapter中数据源的方法,并在这个方法中,移除所有FragmentManager中缓存的Fragment,注意此时的提交方法必须使用commitAllowingStateLoss(),如果使用普通的commit()的话可能会报出java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState 这个错误,出现这个问题的原因是因为在当Activity变的”容易”被销毁时,系统会自动调用onSaveInstanceState方法。

    public void setFragments(ArrayList<Fragment> fragments) {        if (this.list != null) {            FragmentTransaction ft = fm.beginTransaction();            for (Fragment f : this.list) {                ft.remove(f);            }            ft.commitAllowingStateLoss();            ft = null;            fm.executePendingTransactions();        }        this.list = fragments;        notifyDataSetChanged();    }

方法2:复写父类的instantiateItem的方法。该方法返回的是container所对应的一个Key。
在复写了该方法之后我们首先拿到他缓存中的Fragment,然后在拿到Fragmetn所对应·的Tag。然后我们根据fragmentsUpdateFlag数组中的boolean值来判断该Fragment是否需要更新,如果需要更新则移除掉原来的Fragment并把新的Fragment添加进去。

    boolean[] fragmentsUpdateFlag = new boolean[list.size()];    @Override    public Object instantiateItem(ViewGroup container, int position) {        //得到缓存的fragment        Fragment fragment = (Fragment) super.instantiateItem(container,                position);        //得到tag,这点很重要        String fragmentTag = fragment.getTag();        if (fragmentsUpdateFlag[position % fragmentsUpdateFlag.length]) {            //如果这个fragment需要更新            FragmentTransaction ft = fm.beginTransaction();            //移除旧的fragment            ft.remove(fragment);            //换成 新的fragment            fragment = list.get(position % list.size());            //添加新fragment时必须用前面获得的tag,这点很重要            ft.add(container.getId(), fragment, fragmentTag);            ft.attach(fragment);            ft.commit();            //复位更新标志            fragmentsUpdateFlag[position % fragmentsUpdateFlag.length] = false;        }        return fragment;    }

上面两种方法,第一种是直接粗暴的移除了缓存中所有的Fragment,第二种方法则是根据需要来选择移除,所以一般来说推荐使用第二种方法。


1 0
原创粉丝点击