关于切换Fragment的不重新实例化的解决方法

我一般用的replace()方法去切换Fragment,当你只写静态页面的时候是看不出什么区别的,可当你和服务器交互时你就会发现,即便是已经显示过的Fragment还是会被重新实例化,因为replace是会先remove然后add的,所以每次都会执行onDestroyView方法、onCreateView方法。

怎样做到不用重新实例化呢?查阅资料得知,说是用hide和show来显示隐藏,所以首先我想的是直接在xml中直接先定义,然后我就这么做了,然后是通过mFragmentManager.findFragment()来找布局文件中定义的fragment,但是却爆出了Attempt to write to field 'int android.app.Fragment.mNextAnim' on a null obj这个错误,再次查阅资料,有的说是用tag来找,于是我也这么干了,结果没什么用,一样的错误,跑了断点,发现是fragment为空!Why?为什么没找到!然后我才发现我这个上下文有点不一样,我是在fragment中又嵌套了fragment,我想,可能是FragmentManager是属于Activity的上下文的,所以会找不到。于是我试了试,在Activity的xml中定义了一个fragment,然后果然找到了!如果我的环境限制必须要在fragment中嵌套fragment而且还要能不重新实例化,这样的话该怎么实现呢?

我能想到的只有在代码中动态的add了,思路是,第一次判断有没有这个fragment(按照tag找),如果没有就add一个,如果有就show,都要同时把其他的hide。

Fragment fragment1;
Fragment fragment2;
transaction = mFragmentManager.beginTransaction();
switch (checkedId) {
    case R.id.tab1:
        //先找到要显示的fragment
        fragment1 = mFragmentManager.findFragmentByTag("tab1");
        //因为在onCreateView的时候已经把第一个add进去默认显示了,所以不用判断是否为空
        if (fragment1.isAdded())
            transaction.show(fragment1);
        //找到要隐藏的,如果为空,则不需要隐藏
        fragment2 = mFragmentManager.findFragmentByTag("tab2");
        if (fragment2!=null){
            if (fragment2.isAdded())
                transaction.hide(fragment2);
        }

        break;
    case R.id.tab2:
        //先找到要隐藏的fragment
        fragment1 = mFragmentManager.findFragmentByTag("tab1");
        //因为在onCreateView的时候已经把第一个add进去默认显示了,所以不用判断是否为空,直接hide
        if (fragment1.isAdded())
            transaction.hide(fragment1);
        //找到要显示的fragment,如果能找到就show,找不到就add
        fragment2 = mFragmentManager.findFragmentByTag("tab2");
        if (fragment2!=null){
            if (fragment2.isAdded())
                transaction.show(fragment2);
        }else{
            Fragment fragment = new Main2_Tab2();
            transaction.add(R.id.tab_container, fragment, "tab2");
        }
        break;
}
transaction.commit();
我这里写的只是适合我的此时环境,规范上应该每次找到的fragment都应该判断是否为空。

初始化的时候add进第一个要显示的fragment:

private void init() {
    //一开始加载第一个Fragment
    Fragment fragment1 = new Main2_Tab1();
    transaction = mFragmentManager.beginTransaction();
    transaction.add(R.id.tab_container, fragment1, "tab1").commit();
}
值得注意的是,每次add都要指定tag,这样才能依靠tag找到fragment。

那如果fragment多的时候我每次hide的时候都要hide好多fragment,这样代码未免太冗余,我是这么解决的:

首先,初始化的时候一样:

//默认首先显示第一个Fragment
mFragment = new Main_1();
mFragmentManager.beginTransaction().add(R.id.container, mFragment, "bottom1").commit();
然后在需要监听的方法里,比如onClick里切换

mTransaction = mFragmentManager.beginTransaction();
//要添加的新的fragment
Fragment frag ;
//已添加的
Fragment mFr;

switch (v.getId()) {
    case R.id.main_bottom_button_first:
        //切换到第一页
        mFr = mFragmentManager.findFragmentByTag("bottom1");
        frag = new Main_1();
        switchFragment(mFr,frag,"bottom1");
        //                Log.d("Main", "切换到: "+"1Fragment");
        break;
    case R.id.main_bottom_button_second:
        //切换到第二页
        mFr = mFragmentManager.findFragmentByTag("bottom2");
        frag = new Main_2();
        switchFragment(mFr, frag, "bottom2");
        //                Log.d("Main", "切换到: " + "2Fragment");
        break;
    case R.id.main_bottom_button_third:
        //切换到第三页
        mFr = mFragmentManager.findFragmentByTag("bottom3");
        frag = new Main_3();
        switchFragment(mFr, frag, "bottom3");
        //                Log.d("Main", "切换到: " + "3Fragment");
        break;
    case R.id.main_bottom_button_four:
        //切换到第四页
        mFr = mFragmentManager.findFragmentByTag("bottom4");
        frag = new Main_4();
        switchFragment(mFr, frag, "bottom4");
        //                Log.d("Main", "切换到: " + "4Fragment");
        break;
    case R.id.main_bottom_button_five:
        //切换到第四页
        mFr = mFragmentManager.findFragmentByTag("bottom5");
        frag = new Main_5();
        switchFragment(mFr, frag, "bottom5");
        //                Log.d("Main", "切换到: " + "5Fragment");
        break;
}
switchFragment()方法是关键,第一个参数是find的那个Fragment(要显示的),第二个参数是要add的Fragment(要显示的),第三个参数是tag(为了add时使用):

/*
    同一显示隐藏所有的Fragment
*/
    private void switchFragment(Fragment mFr,Fragment frag,String tag) {
        //隐藏其他的Fragment
        Fragment b1 = mFragmentManager.findFragmentByTag("bottom1");
        if (b1 != null) {
            if (b1.isAdded())
                mTransaction.hide(b1);
        }
        Fragment b2 = mFragmentManager.findFragmentByTag("bottom2");
        if (b2 != null) {
            if (b2.isAdded())
                mTransaction.hide(b2);
        }
        Fragment b3 = mFragmentManager.findFragmentByTag("bottom3");
        if (b3 != null) {
            if (b3.isAdded())
                mTransaction.hide(b3);
        }
        Fragment b4 = mFragmentManager.findFragmentByTag("bottom4");
        if (b4 != null) {
            if (b4.isAdded())
                mTransaction.hide(b4);
        }
        Fragment b5 = mFragmentManager.findFragmentByTag("bottom5");
        if (b5 != null) {
            if (b5.isAdded())
                mTransaction.hide(b5);
        }
        if (mFr==null)
            mTransaction.add(R.id.container,frag,tag);
        else{
            mTransaction.show(mFr);
        }
        mTransaction.commit();
    }
首先隐藏所有已经存在的Fragment,然后判断要显示的Fragment找没找到,找到了就show,没找到就add,最后统一commit,我试过每次add、hide或者show都commit,发现会报错 java.lang.IllegalStateException:Cannot forward a response that is already committed

还有,

CompoundButton.OnCheckedChangeListener

这个监听器如果你这同一个RadioGroup里的RadioButton都添加了这个监听,则每个按钮的check改变都会执行这个方法,对于切换fragment来说就会造成混乱,你可以只给这一组里的某一个RadioButton设置这个监听试试,毕竟其他的都是联动影响的(我没试过)。我发现RadioGroup也有一个监听接口

RadioGroup.OnCheckedChangeListener

实现这个接口去根据id判断当前是哪一个RadioButton然后去切换就好了。

如果你是在Activity中切换fragment,那么直接在xml中定义,然后show、hide应该也没问题,并且那样的话不用去频繁的判断是否为空、add等操作,会比较简洁,但是对于在Fragment中又嵌套了Fragment的情况就不能那么做了。

这是目前我能想到的解决Fragment不用重新实例化的最好方法了,欢迎大神提供更简洁的方法。

注意注意,如果我需要在fragment初始化的时候想做一些事情,比如我这里,想要在fragment被hide的时候让切换ViewPager的timer取消,等到再次显示时继续自动切换怎么办,要知道此时hide和show都不会执行任何正常的生命周期方法,哪怕是onPause和onResume,此时Fragment只能重写一个方法

@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    if (hidden){
        if (mTimer != null) {
            mTimer.cancel();
            mTimer = null;
        }
    }else{
        //开启新线程去后台自动切换ViewPager
        mTimer = new Timer();
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                //Handler去循环切换pager
                mHandler.sendEmptyMessage(0);
            }
        }, 3000, 3000);
    }
}
通过hidden来判断隐藏或者显示(hidden为true是隐藏)的时候需要做的事!!

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值