fragment使用技巧

以前写多个fragment切换是经常使用这种方法切换fragment:

/**
  * 使用replace切换页面
  * 显示fragment
  */
 private void showFragment(Fragment fg){

     FragmentTransaction transaction = fragmentManager.beginTransaction();
     transaction.replace(R.id.content, fg);
     transaction.commit();

 }

replace():该方法只是在上一个Fragment不再需要时采用的简便方法,弊端就是如果需要重复使用该fragment时,需要每次都要重新加载一次。比如我在第一个fragment输入信息后,切换第二个fragment后再切换回去,就会造成数据丢失

而且,如果每切换一次就实例化一次的话,FragmentManager管理下的栈也会爆满,最终会导致手机卡顿,这很明显不是正确的Fragment使用姿势,这时,我们就需要使用show()、hide()、add()了,正确的切换方式是add(),切换时hide(),add()另一个Fragment;再次切换时,只需hide()当前,show()另一个就行了,代码修改如下:


/**
  * 使用show() hide()切换页面
  * 显示fragment
  */
 private void showFragment(Fragment fg){

     FragmentTransaction transaction = fragmentManager.beginTransaction();

     //如果之前没有添加过
     if(!fg.isAdded()){
         transaction
                 .hide(currentFragment)
                 .add(R.id.content,fg);
     }else{
         transaction
                 .hide(currentFragment)
                 .show(fg);
     }

    //全局变量,记录当前显示的fragment
     currentFragment = fg;

     transaction.commit();

 }
即使切换到别的fragment,再切换回来,数据还依然存在,这就避免了Fragment切换时布局重新实例化。

安卓app有一种特殊情况,就是 app运行在后台的时候,系统资源紧张的时候导致把app的资源全部回收(杀死app的进程),这时把app再从后台返回到前台时,app会重启。这种情况下文简称为:“内存重启”。(屏幕旋转等配置变化也会造成当前Activity重启,本质与“内存重启”类似)在系统要把app回收之前,系统会把Activity的状态保存下来,Activity的FragmentManager负责把Activity中的Fragment保存起来。在“内存重启”后,Activity的恢复是从栈顶逐步恢复,Fragment会在宿主Activity的onCreate方法调用后紧接着恢复

当我们不退出软件,只是后台挂着去干别的事,当系统内存不足回收我们这个app时,再切换回来,app的这几个Fragment界面会重叠。

三个fragment全部叠在了一起,而且点击上面菜单也不能消除重叠。显然,这并不是我们想要的,没事,继续解决问题,使用findFragmentByTag:即在add()或者replace()时绑定一个tag,一般我们是用fragment的类名作为tag,然后在发生“内存重启”时,通过findFragmentByTag找到对应的Fragment,并hide()需要隐藏的fragment。,修改如下:

/**
  * 使用show() hide()切换页面
  * 显示fragment
  */
 private void showFragment(Fragment fg){

     FragmentTransaction transaction = fragmentManager.beginTransaction();

     //如果之前没有添加过
     if(!fg.isAdded()){
         transaction
                 .hide(currentFragment)
                 .add(R.id.content,fg,fg.getClass().getName());  //第三个参数为当前的fragment绑定一个tag,tag为当前绑定fragment的类名
     }else{
         transaction
                 .hide(currentFragment)
                 .show(fg);
     }

     currentFragment = fg;

     transaction.commit();

 }
别急,还没完,在当前Activity的 onCreate()方法里面添加一下代码:
if (savedInstanceState != null) { // “内存重启”时调用

   //从fragmentManager里面找到fragment
   fgOne = (OneFragment) fragmentManager.findFragmentByTag(OneFragment.class.getName());
   fgTwo = (TwoFragment) fragmentManager.findFragmentByTag(TwoFragment.class.getName());
   fgThree = (ThreeFragment) fragmentManager.findFragmentByTag(ThreeFragment.class.getName());

   //解决重叠问题show里面可以指定恢复的页面
   fragmentManager.beginTransaction()
           .show(fgOne)
           .hide(fgTwo)
           .hide(fgThree)
           .commit();

   //把当前显示的fragment记录下来
   currentFragment = fgOne;

}else{      //正常启动时调用

   fgOne = new OneFragment();
   fgTwo = new TwoFragment();
   fgThree = new ThreeFragment();

   showFragment(fgOne);
}

OK,当app后台时遇到“内存重启”的情况下,再返回我们的app,就会恢复到 show(fgOne)页面,而且还不会造成重叠问题!


很显然,这样结束是不道德的,因为有人会问了,如果想记录当前退出的状态以至于下次恢复时直接显示之前的fragment页面怎么办,恩,对于这个问题,我们可以在activity的onSaveInstanceState()方法中记录一下“内存重启”之前的Fragment的页面,然后在oncreate()中取出来,根据保存的页面来显示到指定的fragment,代码如下:

@Override
protected void onSaveInstanceState(Bundle outState) {

    //“内存重启”时保存当前的fragment名字
    outState.putString(STATE_FRAGMENT_SHOW,currentFragment.getClass().getName());
    super.onSaveInstanceState(outState);
}
然后在 oncreate()方法中添加(修改上面的那个代码)

if (savedInstanceState != null) { // “内存重启”时调用

    //获取“内存重启”时保存的fragment名字
    String saveName = savedInstanceState.getString(STATE_FRAGMENT_SHOW);

    //从fragmentManager里面找到fragment
    fgOne = (OneFragment) fragmentManager.findFragmentByTag(OneFragment.class.getName());
    fgTwo = (TwoFragment) fragmentManager.findFragmentByTag(TwoFragment.class.getName());
    fgThree = (ThreeFragment) fragmentManager.findFragmentByTag(ThreeFragment.class.getName());

    //如果为空就默认操作
    if(TextUtils.isEmpty(saveName)){
        //解决重叠问题
        fragmentManager.beginTransaction()
                .show(fgOne)
                .hide(fgTwo)
                .hide(fgThree)
                .commit();

        //把当前显示的fragment记录下来
        currentFragment = fgOne;

    }else{

        if(saveName.equals(fgOne.getClass().getName())){    //如果推出之前是OneFragment

            //解决重叠问题
            fragmentManager.beginTransaction()
                    .show(fgOne)
                    .hide(fgTwo)
                    .hide(fgThree)
                    .commit();

            //把当前显示的fragment记录下来
            currentFragment = fgOne;

        }else if(saveName.equals(fgTwo.getClass().getName())){  //如果推出之前是TwoFragment

            //解决重叠问题
            fragmentManager.beginTransaction()
                    .show(fgTwo)
                    .hide(fgOne)
                    .hide(fgThree)
                    .commit();

            //把当前显示的fragment记录下来
            currentFragment = fgTwo;

        }else{    //如果推出之前是ThreeFragment

            //解决重叠问题
            fragmentManager.beginTransaction()
                    .show(fgThree)
                    .hide(fgTwo)
                    .hide(fgOne)
                    .commit();

            //把当前显示的fragment记录下来
            currentFragment = fgThree;

        }

    }


}else{      //正常启动时调用

    fgOne = new OneFragment();
    fgTwo = new TwoFragment();
    fgThree = new ThreeFragment();

    showFragment(fgOne);
}
OK,这样就可以了,我们通过保存当前显示的fragment的类名,当我们在第二个fragment页面时后台,等到“内存重启”后返回该app时,就根据之前保存的类名来判断加载指定的fragment,而且,重叠的问题也解决了!

当然,有网友问了,如果fragment比较多,那么多if多麻烦,是啊,上面的代码主要就是让大家理解一下思路,具体开发不建议那么写,下面,就说一下上面的代码该如何精简吧:





创建一个List用来存所有的fragment,给fragment设置tag可以使用当前的索引index,然后在onSaveInstanceState中保存当前的索引,在恢复时通过索引来找到fragment

全部代码修改精简后如下:::

//当前显示的fragment
private static final String CURRENT_FRAGMENT = "STATE_FRAGMENT_SHOW";

private TextView tvone;
private TextView tvtwo;
private TextView tvthree;
private FragmentManager fragmentManager;

private Fragment currentFragment = new Fragment();
private List<Fragment> fragments = new ArrayList<>();

private int currentIndex = 0;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    this.tvthree = (TextView) findViewById(R.id.tv_three);
    this.tvtwo = (TextView) findViewById(R.id.tv_two);
    this.tvone = (TextView) findViewById(R.id.tv_one);

    fragmentManager = getSupportFragmentManager();

    tvthree.setOnClickListener(this);
    tvtwo.setOnClickListener(this);
    tvone.setOnClickListener(this);


    if (savedInstanceState != null) { // “内存重启”时调用

        //获取“内存重启”时保存的索引下标
        currentIndex = savedInstanceState.getInt(CURRENT_FRAGMENT,0);

        //注意,添加顺序要跟下面添加的顺序一样!!!!
        fragments.removeAll(fragments);
        fragments.add(fragmentManager.findFragmentByTag(0+""));
        fragments.add(fragmentManager.findFragmentByTag(1+""));
        fragments.add(fragmentManager.findFragmentByTag(2+""));

        //恢复fragment页面
        restoreFragment();


    }else{      //正常启动时调用

        fragments.add(new OneFragment());
        fragments.add(new TwoFragment());
        fragments.add(new ThreeFragment());

        showFragment();
    }

}

@Override
protected void onSaveInstanceState(Bundle outState) {

    //“内存重启”时保存当前的fragment名字
    outState.putInt(CURRENT_FRAGMENT,currentIndex);
    super.onSaveInstanceState(outState);
}

@Override
public void onClick(View v) {

    switch (v.getId()){

        case R.id.tv_one:

            currentIndex = 0;

            break;
        case R.id.tv_two:

            currentIndex = 1;

            break;
        case R.id.tv_three:

            currentIndex = 2;

            break;

    }

    showFragment();

}


/**
 * 使用show() hide()切换页面
 * 显示fragment
 */
private void showFragment(){

    FragmentTransaction transaction = fragmentManager.beginTransaction();

    //如果之前没有添加过
    if(!fragments.get(currentIndex).isAdded()){
        transaction
                .hide(currentFragment)
                .add(R.id.content,fragments.get(currentIndex),""+currentIndex);  //第三个参数为添加当前的fragment时绑定一个tag

    }else{
        transaction
                .hide(currentFragment)
                .show(fragments.get(currentIndex));
    }

    currentFragment = fragments.get(currentIndex);

    transaction.commit();

}

/**
 * 恢复fragment
 */
private void restoreFragment(){


    FragmentTransaction mBeginTreansaction = fragmentManager.beginTransaction();

    for (int i = 0; i < fragments.size(); i++) {

        if(i == currentIndex){
            mBeginTreansaction.show(fragments.get(i));
        }else{
            mBeginTreansaction.hide(fragments.get(i));
        }

    }

    mBeginTreansaction.commit();

    //把当前显示的fragment记录下来
    currentFragment = fragments.get(currentIndex);

}


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大学生参加学科竞赛有着诸多好处,不仅有助于个人综合素质的提升,还能为未来职业发展奠定良好基础。以下是一些分析: 首先,学科竞赛是提高专业知识和技能水平的有效途径。通过参与竞赛,学生不仅能够深入学习相关专业知识,还能够接触到最新的科研成果和技术发展趋势。这有助于拓展学生的学科视野,使其对专业领域有更深刻的理解。在竞赛过程中,学生通常需要解决实际问题,这锻炼了他们独立思考和解决问题的能力。 其次,学科竞赛培养了学生的团队合作精神。许多竞赛项目需要团队协作来完成,这促使学生学会有效地与他人合作、协调分工。在团队合作中,学生们能够学到如何有效沟通、共同制定目标和分工合作,这对于日后进入职场具有重要意义。 此外,学科竞赛是提高学生综合能力的一种途径。竞赛项目通常会涉及到理论知识、实际操作和创新思维等多个方面,要求参赛者具备全面的素质。在竞赛过程中,学生不仅需要展现自己的专业知识,还需要具备创新意识和解决问题的能力。这种全面的综合能力培养对于未来从事各类职业都具有积极作用。 此外,学科竞赛可以为学生提供展示自我、树立信心的机会。通过比赛的舞台,学生有机会展现自己在专业领域的优势,得到他人的认可和赞誉。这对于培养学生的自信心和自我价值感非常重要,有助于他们更加积极主动地投入学习和未来的职业生涯。 最后,学科竞赛对于个人职业发展具有积极的助推作用。在竞赛中脱颖而出的学生通常能够引起企业、研究机构等用人单位的关注。获得竞赛奖项不仅可以作为个人履历的亮点,还可以为进入理想的工作岗位提供有力的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值