切换多国语言导致Fragment被回收,出现切换错乱
如图所示,切换多国语言之后,由于fragment被回收,再次进入app的时候,切换fragment会错乱,甚至有可能切换完全无效果。
在百度google之后,发现只有这个和我现在遇到的问题比较像,但是代码却不是很完善。
在切换语言时,activity会被系统回收后重新创建,
此时原先依附于该activity的fragment也会被系统destroy掉,
但系统会自动创建新的fragment的实例attach到新的activity中,
若此时用户在activity中手动创建fragment,则会导致程序fragment管理混乱.
因此,建议用如下方法创建fragment
public class FragmentFactory {
public static Fragment getFragmentByTag(Context pContext,String pTag){
FragmentManager fm = pContext.getSupportFragmentManager();
//查找是否已存在,已存在则不需要重发创建,切换语言时系统会自动重新创建并attch,无需手动创建
Fragment fragment = fm.findFragmentByTag(pTag);
if (fragment != null){
return fragment;
}
else {
if(MyFragment.TAG.equals(pTAG)){
return new MyFragment();
}
}
}
}
于是开始了自己的修改之路。
代码如下(我的切换fragment等操作放在BaseActivity里面,BaseActivity是继承的FragmentActivity):
//利用反射获取fragment实例,如果该实例已经创建,则用不再创建。
protected Fragment getFragmentInstance(Class fragmentClass) {
FragmentManager fm = getSupportFragmentManager();
//查找是否已存在,已存在则不需要重发创建,切换语言时系统会自动重新创建并attch,无需手动创建
Fragment fragment = fm.findFragmentByTag(fragmentClass.getSimpleName());
if (fragment != null) {
return fragment;
} else {
try {
fragment = (Fragment) fragmentClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return fragment;
}
}
/**
* fragment切换
*
* @param fragment
* @param layoutId
* @return 最后显示的fragment
*/
protected void switchFragment(Fragment fragment, int layoutId) {
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager == null || layoutId <= 0) {
return;
}
//全局的fragment对象,用于记录最后一次切换的fragment(当前展示的fragment)
mLastFragment = fragment;
this.layoutId = layoutId;
FragmentTransaction transaction = fragmentManager.beginTransaction();
if (fragment != null) {
List<Fragment> fragments = fragmentManager.getFragments();
//先判断是否为空
if (fragments != null) {
for (Fragment mfragment : fragments) {
//再把现在显示的fragment 隐藏掉
if (!mfragment.isHidden()) {
transaction.hide(mfragment);
}
}
}
//显示现在的fragment
if (!fragment.isAdded()) {
// transaction.addToBackStack(null);
transaction.add(layoutId, fragment, fragment.getClass().getSimpleName());
} else {
transaction.show(fragment);
}
}
transaction.commit();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
//存储最后一个fragment对象
if (mLastFragment != null) {
outState.putString("fragment", mLastFragment.getClass().getName());
outState.putInt("layoutId", layoutId);
}
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
//取出该对象,由于此时所有的fragment是处于onAttach,hidden状态,所以只需要将它show出来就可以了。
String fragmentSimpleName = savedInstanceState.getString("fragment");
if (!TextUtils.isEmpty(fragmentSimpleName)) {
try {
Class mClass = Class.forName(fragmentSimpleName);
//但是不能创建新fragment对象,因为之前的fragment并没有被回收掉,所以需要取出fragmentManager中的fragment,否则会造成错乱
mLastFragment = getFragmentInstance(mClass);
if (mLastFragment.isHidden()) {
switchFragment(mLastFragment, savedInstanceState.getInt("layoutId"));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
效果如下:
但是发现了,虽然fragment没有错乱,但是fragment数据错乱了(订单)。
这是由于在activity被回收,重建的过程中,fragment虽然也被回收,但是并不是完全回收。然后show这个fragment,显示的不完全fragment。
当然,也有解决办法,而且非常简单。
在fragment中的onCreate()加上这句
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
setRetainInstance这个方法可以保存fragment实例,特别是在Activity重建的时候特别有用。
而设置这个属性为true的时候,fragment的生命周期发生了变化
可见它并不会执行onCreate()和onDestroy(),所以切忌在设置这个属性之后,还在onCreate()和onDestroy()里面进行操作。
官方说明
/**
* Control whether a fragment instance is retained across Activity
* re-creation (such as from a configuration change). This can only
* be used with fragments not in the back stack. If set, the fragment
* lifecycle will be slightly different when an activity is recreated:
* <ul>
* <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
* will be, because the fragment is being detached from its current activity).
* <li> {@link #onCreate(Bundle)} will not be called since the fragment
* is not being re-created.
* <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b>
* still be called.
* </ul>
*/
控制一个fragment实例在Activity重建的时候是否保留(比如说fragment里的东西变了的时候),这个仅仅只能用于fragment没有在回退栈中。如果设置为true,在activity重建的时候,这个fragment的生命周期将会发生变化。
activity重建的时候,fragment周期的变化:
onDestroy:不会执行,但是onDetach会始终执行。
onCreate():不会执行。因为fragment不会被重建。
onAttach()和onActivityCreat():始终会执行。
最终效果如下:
ps.
1、Fragment和FragmentManager是用的v4包的,android.support.v4.app.FragmentManager。否则没有getFragments()方法
2、由于我是用的RadioGroup作为TabHost,在Activity被回收之后重新进去还会保存上一个tab选中的状态,所以在onRestoreInstanceState只需要恢复Fragment就行了。如果是用LinearLayout或者TextView或者其他的,在activity重新进去之后没有保存tab选中的状态,那么需要在activity中重写onRestoreInstanceState,获取baseActivity的mLastFragment,判断它是属于哪个tab,再改变该tab按钮状态。
更多推荐


所有评论(0)