原因:
使用 Fragment 的状态保存,当系统内存不足,Fragment 的宿主 Activity 回收的时候,
Fragment 的实例并没有随之被回收。Activity 被系统回收时,会主动调用 onSaveInstance()
方法来保存视图层(View Hierarchy),所以当 Activity 通过导航再次被重建时,
之前被实例化过的 Fragment 依然会出现在 Activity 中,此时的 FragmentTransaction 中的
相当于又再次 add 了 fragment 进去的,hide()和show()方法对之前保存的fragment已经
失效了。综上这些因素导致了多个Fragment重叠在一起。
解决方法:
第一种
1.给每个 Fragment 加一个 Tag
2.在 onCreate(Bundle savedInstanceState) 中判断 Bundle savedInstanceState 是否不为空
3.不为空则进行 find Tag,重新给几个 frament 赋值
这样子仍是对之前保存的 fragment 操作,成功解决了重叠的问题。
kotlin写法
class MainActivity : AppCompatActivity() {
private var mHomeFragment: HomeFragment? = null
private var mMessageFragment: MessageFragment? = null
private var mMineFragment: MineFragment? = null
private var mPreFragment: Fragment? = null
private lateinit var mTransaction: FragmentTransaction
override fun onCreate(savedInstanceState: Bundle?) {
mTransaction = supportFragmentManager.beginTransaction()
//savedInstanceState不为空, 状态恢复时, 重新查找一遍, 重新赋值
savedInstanceState?.let {
mHomeFragment = mHomeFragment ?: supportFragmentManager.findFragmentByTag("0") as HomeFragment
mMessageFragment = mMessageFragment ?: supportFragmentManager.findFragmentByTag("1") as MessageFragment
mMineFragment = mMineFragment ?: supportFragmentManager.findFragmentByTag("2") as MineFragment
}
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initData()
}
private fun initData() {
//选中监听
mRgBottomMenu.setOnCheckedChangeListener { _, checkedId ->
val transaction = supportFragmentManager.beginTransaction()
mPreFragment?.let {
transaction.hide(it)
}
when (checkedId) {
R.id.mRbHome -> //home界面
mHomeFragment =
mHomeFragment?.apply { showFragment(transaction, this) }
?: addFragment(transaction, 0) as HomeFragment
R.id.mRbMessage ->//消息界面
mMessageFragment =
mMessageFragment?.apply { showFragment(transaction, this) }
?: addFragment(transaction, 1) as MessageFragment
R.id.mRbMine -> //我的界面
mMineFragment =
mMineFragment?.apply { showFragment(transaction, this) }
?: addFragment(transaction, 2) as MineFragment
}
transaction.commitAllowingStateLoss()
}
mRbHome.isChecked = true
}
//显示已经存在的fragment
private fun showFragment(transaction: FragmentTransaction, fragment: Fragment) {
transaction.show(fragment)
mPreFragment = fragment
Log.d("test:: ", "show: ${fragment::class.java.simpleName} - $fragment")
}
//添加第一次创建实例的fragment
private fun addFragment(transaction: FragmentTransaction, type: Int): Fragment {
val fragment = when (type) {
0 -> HomeFragment()
1 -> MessageFragment()
else -> MineFragment()
}
transaction.add(R.id.mFlRoot, fragment, "$type")
mPreFragment = fragment
Log.d("test:: ", "add: ${fragment::class.java.simpleName} - $fragment")
return fragment
}
}
java写法
public class MainActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener {
private TextView mTvTitle;
private LinearLayout mLlActionbar;
private FrameLayout mFlRoot;
private RadioButton mRbHome;
private RadioButton mRbMessage;
private RadioButton mRbMine;
private RadioGroup mRgBottomMenu;
private HomeFragment mHomeFragment;
private MessageFragment mMessageFragment;
private MineFragment mMineFragment;
private Fragment mPreFragment;
private FragmentManager mFragmentManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
mFragmentManager = getSupportFragmentManager();
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mHomeFragment = (HomeFragment) mFragmentManager.findFragmentByTag("HomeFragment");
mMessageFragment = (MessageFragment) mFragmentManager.findFragmentByTag("MessageFragment");
mMineFragment = (MineFragment) mFragmentManager.findFragmentByTag("MineFragment");
}
setContentView(R.layout.activity_main);
initView();
initData();
initListener();
}
//可要可不要
@Override
public void onAttachFragment(@NonNull Fragment fragment) {
// if (mHomeFragment == null && fragment instanceof HomeFragment) {
// mHomeFragment = (HomeFragment) fragment;
// } else if (mMessageFragment == null && fragment instanceof MessageFragment) {
// mMessageFragment = (MessageFragment) fragment;
// } else if (mMineFragment == null && fragment instanceof MineFragment) {
// mMineFragment = (MineFragment) fragment;
// }
}
private void initView() {
mTvTitle = findViewById(R.id.mTvTitle);
mLlActionbar = findViewById(R.id.mLlActionbar);
mFlRoot = findViewById(R.id.mFlRoot);
mRbHome = findViewById(R.id.mRbHome);
mRbMessage = findViewById(R.id.mRbMessage);
mRbMine = findViewById(R.id.mRbMine);
mRgBottomMenu = findViewById(R.id.mRgBottomMenu);
}
private void initData() {
}
private void initListener() {
mRgBottomMenu.setOnCheckedChangeListener(this);
// mRgBottomMenu.check(R.id.mRbHome);
mRbHome.setChecked(true);
}
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
FragmentTransaction transaction = mFragmentManager.beginTransaction();
if (mPreFragment != null) {
transaction.hide(mPreFragment);
}
switch (checkedId) {
case R.id.mRbHome:
if (mHomeFragment == null) {
mHomeFragment = new HomeFragment();
transaction.add(R.id.mFlRoot, mHomeFragment, "HomeFragment");
} else {
transaction.show(mHomeFragment);
}
mPreFragment = mHomeFragment;
break;
case R.id.mRbMessage:
if (mMessageFragment == null) {
mMessageFragment = new MessageFragment();
transaction.add(R.id.mFlRoot, mMessageFragment, "MessageFragment");
} else {
transaction.show(mMessageFragment);
}
mPreFragment = mMessageFragment;
break;
case R.id.mRbMine:
if (mMineFragment == null) {
mMineFragment = new MineFragment();
transaction.add(R.id.mFlRoot, mMineFragment, "MineFragment");
} else {
transaction.show(mMineFragment);
}
mPreFragment = mMineFragment;
break;
}
transaction.commitAllowingStateLoss();
}
}
第二种
Activity 中的 onSaveInstanceState() 里面有一句super.onSaveInstanceState(outState);,
Google 对于这句话的解释是 “Always call the superclass so it can save the view hierarchy state”,
大概意思是“总是执行这句代码来调用父类去保存视图层的状态”。通过注释掉这句话,
这样主 Activity 因为种种原因被回收的时候就不会保存之前的 fragment state,
也可以成功解决重叠的问题。
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
// super.onSaveInstanceState(outState);
}
fragment 回退栈有关问题
通常使用fragmentManager.beginTransaction().addToBackStack(null);
但是也经常会遇到说“某个fragment”已经被添加的错误,这往往是我们在对fragment进行返回处理时出现问题。这样的问题最常出现在我们点击界面进行跳转的时候。
一般情况,当我们点击导航栏进行界面切换之后,之前back stack中保存的fragment也就不再需要保留了。用下面的语句可以将back stack清空,同时也可避免上述错误的出现。
FragmentManager fragmentManager=getSupportFragmentManager();
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
网友评论