Fragment重叠及常见问题

文章目录

首先看看
transaction中的几个方法:

  • replace: 显示和隐藏fragemnt
  • add: 将一个Fragment添加到Activity
  • hiddenshow:显示和隐藏fragemnt

onResume和onPause应用技巧

这两个函数只是跟随activity的生命周期,并不跟随着fragment的生命周期,通常应该避免在ActivityonResume中进行网络请求或其他逻辑,应为当App在进行前后台切换的时候,onResume均会调用。
显示和隐藏Fragment时,可以采用showhidden组合,也可以使用replace

//针对replace的处理方法是使用setUserVisibleHint
@Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
            //相当于Fragment的onResume
        } else {
            //相当于Fragment的onPause
        }
    }
//而针对hide/show处理方法是使用onHiddenChanged
@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    if(hidden){
        //TODO now visible to user
    } else {
        //TODO now invisible to user
    }
}

Fragment重叠

为什么会出现界面重叠呢?

当我们离开Activity的时候,切换到别的APP的时候,当内存不够用,Fragment所在Activity被销毁,会调用onSaveInstanceState()方法,Fragment都会被保存起来,当我再次回到这个app的时候,通过onCreate中的参数savedInstanceState恢复了之前的fragment,就导致了界面重叠。

replace

使用replace方式,虽然这种方式会避免上述的bug,但也是重复创建了对象,每次replace会把生命周期全部执行一遍,它会销毁视图,重新加载,如果在这些生命周期函数 里拉取数据的话,就会不断重复的加载刷新数据。

add [show, hidden]

  1. add的时候,加上一个tag参数
transaction.add(R.id.content, IndexFragment,”Tab1″);
  1. 然后当IndexFragment引用被回收置空的话,先通过
IndexFragment=FragmentManager.findFragmentByTag(“Tab1″);

找到对应的引用,然后继续上面的hide,show;

Fragment中getActivity()的空指针问题

这个问题的原因大部分在于Fragment已经和所在activity解除了关联,也就是调用了onDetach()方法。
解决方法:
我们可以在Fragment中定义Activity成员变量,当ActivityFragment关联的时候,给Activity赋值,这样即使ActivityFragment解除关联后,成员变量的地址任然指向Activity

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    this.mActivity = (Activity)context;
}

Fragment State Loss

Fragment的作用域是ActivityFragmentManager管理着一个Activity所有的Fragment,这些Fragment被放入一个栈中。每个Fragment有一个FragmentState,它相当于Fragmentsnapshot,保存状态时FragmentManager把每个FragmentFragmentState存储起来,最终存储到ActivitysavedInstanceState中。
既然状态的保存与恢复都必须要把Fragment带上,那么一旦当Fragment的状态已保存过了,那么就不应该再改变Fragment的状态。因此FragmentManager的每一个操作前,都会调用一个方法来检查状态是否保存过了:

private void checkStateLoss() {
    if (mStateSaved) {
        throw new IllegalStateException(
                    "Can not perform this action after onSaveInstanceState");
    }
    if (mNoTransactionsBecause != null) {
        throw new IllegalStateException(
                    "Can not perform this action inside of " + mNoTransactionsBecause);
    }
}

原因就是commit操作发生在了状态保存之后,大招就是使用commitAllowStateLoss

Activity#onBackPressed

还有一种情况,也会出现此异常,而且是在Activity中完全 没有Fragment的情况下,所以解决办法是:同样设置一个标志,如果状态已保存过,就不要再处理onBackPressed。

public class FragmentStateLossActivity extends Activity {
    private static final String TAG = "Fragment state loss";
    private boolean mStateSaved;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_state_loss);
        mStateSaved = false;
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        // Not call super won't help us, still get crash
        super.onSaveInstanceState(outState);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            mStateSaved = true;
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        mStateSaved = false;
    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mStateSaved = true;
    }

    @Override
    protected void onStart() {
        super.onStart();
        mStateSaved = false;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (!mStateSaved) {
            return super.onKeyDown(keyCode, event);
        } else {
            // State already saved, so ignore the event
            return true;
        }
    }

    @Override
    public void onBackPressed() {
        if (!mStateSaved) {
            super.onBackPressed();
        }
    }
}

参考链接:

你真的会用 Fragment 了么?-Fragment 解析
让你不再俱怕 Fragment State Loss
Fragment销毁时replace和add两个方法的区别

点赞