1、Fragment重叠的原因
1.support库24.0.0之前有一个bug,就是在FragmentManager保存Fragment实例状态的时候,没有保存mHidden, 因此重创建之后Fragment都处于显示状态就造成了重叠
2.虽然我在24.0.0之后在activity内存重启重新创建的时候FragmentManager帮我们保存了mHidden属性,但是如果我们在初始化Fragment也同样会出现重叠现象。
private void initFragment() {
fragmentOne = new FragmentOne();
fragmentTwo = new FragmentTwo();
fragmentThree = new FragmentThree();
fragmentList.add(fragmentOne);
fragmentList.add(fragmentTwo);
fragmentList.add(fragmentThree);
}
我们知道当activity当内存不足销毁重建的时候FragmentManager帮我们保存一些属性并且重新创建fragment实例,由于每次activity重建onCreate方法中我们又实例化了fragment(由于fragment默认是透明的,FragmetMangeager恢复重建的fragment实例与onCreate新创建的实例重叠,隐藏状态不同)导致fragment重叠现象。
2、解决方案
首先对于出现内存重启Activity销毁现象我们不好调试,我们可以在设置开发者选项中把 不保留活动 勾上,这样我们每次按home键在重新返回就能模拟重建环境。
我们先贴一下我们平常出问题的代码
public class MethodTwoActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener {
private RadioGroup mMainTabGroup;
private FragmentOne fragmentOne;
private FragmentTwo fragmentTwo;
private FragmentThree fragmentThree;
private List fragmentList = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_public);
setTitle("方法二");
mMainTabGroup = (RadioGroup) findViewById(R.id.main_tab_group);
mMainTabGroup.setOnCheckedChangeListener(this);
initFragment();
((RadioButton) mMainTabGroup.getChildAt(0)).setChecked(true);
}
private void initFragment() {
fragmentOne = new FragmentOne();
fragmentTwo = new FragmentTwo();
fragmentThree = new FragmentThree();
fragmentList.add(fragmentOne);
fragmentList.add(fragmentTwo);
fragmentList.add(fragmentThree);
}
/**
* 既然恢复的时候走两遍我们就控制一下
*/
private int mCurrentCheckedId;
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (mCurrentCheckedId != checkedId)
mCurrentCheckedId = checkedId;
else {
return;
}
switch (checkedId) {
case R.id.rb_one:
addContentLayout(fragmentOne);
break;
case R.id.rb_two:
addContentLayout(fragmentTwo);
break;
case R.id.rb_three:
addContentLayout(fragmentThree);
break;
default:
break;
}
}
private void addContentLayout(Fragment fragment) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
for (Fragment f : fragmentList) {
if (f != fragment&&f.isAdded()) {
ft.hide(f);
}
}
if (fragment.isAdded()) {
if (fragment.isHidden()) {
ft.show(fragment);
}
} else {
ft.add(R.id.main_content, fragment, fragment.getClass().getSimpleName());
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
}
ft.commit();
}
一、方法一
重写activity的onSaveInstanceState方法 并注释掉super方法。
@Override
protected void onSaveInstanceState(Bundle outState) {
//super.onSaveInstanceState(outState);
}
注释掉此父类方法,activity在销毁的时候不会保存视图等相关数据,当然也不保存fragment的状态数据。
二、方法二
重写activity的onSaveInstanceState方法 并在方法中通过FragmentTransaction移除我们的fragment
@Override
protected void onSaveInstanceState(Bundle outState) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.remove(fragmentOne);
transaction.remove(fragmentTwo);
transaction.remove(fragmentThree);
transaction.commitAllowingStateLoss();
//这个方法会帮我们保留选中位置状态
super.onSaveInstanceState(outState);
}
缺点: 和方法一都是沒有保存Fragment的相关数据,但是我们执行了activity 的 super.onSaveInstanceState(outState);方法,经过debug调试activity会帮我保留选中的位置(恢复的时候底部按钮选中状态可判断)
在我们按home键重新回到activity执行onCreate 通过调试发现 onCheckedChanged方法会走三次,前两次是默认选中((RadioButton) mMainTabGroup.getChildAt(0)).setChecked(true)导致(我们可以通过mCurrentCheckedId变量控制下面逻辑是否走)第三次就是执行activity帮我保留选中的位置onCheckedChanged。
/**
* 既然恢复的时候走两遍我们就控制一下
*/
private int mCurrentCheckedId;
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (mCurrentCheckedId != checkedId)
mCurrentCheckedId = checkedId;
else {
return;
}
switch (checkedId) {
case R.id.rb_one:
addContentLayout(fragmentOne);
break;
case R.id.rb_two:
addContentLayout(fragmentTwo);
break;
case R.id.rb_three:
addContentLayout(fragmentThree);
break;
default:
break;
}
}
三、方法三
直接上代码
public class MethodThreeActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener {
private RadioGroup mMainTabGroup;
private FragmentOne fragmentOne;
private FragmentTwo fragmentTwo;
private FragmentThree fragmentThree;
private List fragmentList = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_public);
setTitle("方法三");
mMainTabGroup = (RadioGroup) findViewById(R.id.main_tab_group);
mMainTabGroup.setOnCheckedChangeListener(this);
if (savedInstanceState != null) {
fragmentOne = (FragmentOne) getSupportFragmentManager().findFragmentByTag(FragmentOne.class.getSimpleName());
fragmentTwo = (FragmentTwo) getSupportFragmentManager().findFragmentByTag(FragmentTwo.class.getSimpleName());
fragmentThree = (FragmentThree) getSupportFragmentManager().findFragmentByTag(FragmentThree.class.getSimpleName());
//这块一定要 这样写如果我们进入界面只添加了fragmentOne 那就会引起空指针异常
if (fragmentOne != null) {
fragmentList.add(fragmentOne);
} else {
fragmentOne = new FragmentOne();
fragmentList.add(fragmentOne);
}
if (fragmentTwo != null) {
fragmentList.add(fragmentTwo);
} else {
fragmentTwo = new FragmentTwo();
fragmentList.add(fragmentTwo);
}
if (fragmentThree != null) {
fragmentList.add(fragmentThree);
} else {
fragmentThree = new FragmentThree();
fragmentList.add(fragmentThree);
}
} else {
initFragment();
}
((RadioButton) mMainTabGroup.getChildAt(0)).setChecked(true);
}
private void initFragment() {
fragmentOne = new FragmentOne();
fragmentTwo = new FragmentTwo();
fragmentThree = new FragmentThree();
fragmentList.add(fragmentOne);
fragmentList.add(fragmentTwo);
fragmentList.add(fragmentThree);
}
/**
* 既然恢复的时候走两遍我们就控制一下
*/
private int mCurrentCheckedId;
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (mCurrentCheckedId != checkedId)
mCurrentCheckedId = checkedId;
else {
return;
}
switch (checkedId) {
case R.id.rb_one:
addContentLayout(fragmentOne);
break;
case R.id.rb_two:
addContentLayout(fragmentTwo);
break;
case R.id.rb_three:
addContentLayout(fragmentThree);
break;
default:
break;
}
}
private void addContentLayout(Fragment fragment) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
for (Fragment f : fragmentList) {
if (f != fragment && f.isAdded()) {
ft.hide(f);
}
}
if (fragment.isAdded()) {
if (fragment.isHidden()) {
ft.show(fragment);
}
} else {
ft.add(R.id.main_content, fragment, fragment.getClass().getSimpleName());
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
}
ft.commit();
}
}
在activity重建的时候如果savedInstanceState != null来判断是否activity重建,通过findFragmentByTag方法找到FragmentManager帮我们恢复重建Fragment(保存了退到后台销毁的Fragment相关视图属性)。这样我们不仅不会多创建没用的实例也保存了fragment视图相关状态信息。
Demo下载
https://github.com/lurenman/FragmentOverlapDemo