彻底解决Fragment重叠的问题

简单粗暴

Posted by lx8421bcd on November 26, 2016

原文由本人于2016年创作于CSDN,后续修改完善提交于此。

在布局应用一级页面时很多App都会采取BottomBar + Fragment切换来构建。一般Fragment会使用FragmentManager管理,就是在一个FrameLayout构成的ContentView上做show和hide。

在这里有一个问题需要注意,在这种有多个按钮处于一个屏幕可以快速切换Fragment的UI布局下,不要使用add和replace去切换fragment,使用show和hide去切换是最好的选择,因为show和hide本质上是在控制Fragment中View的Visiblity,而执行replace则会触发fragment的detach()函数与Activity解除关联,如果此时用户快速点击,在detach还没执行完就执行attach(),很容易产生IllegalStateException:no host;应用直接崩溃。

使用show和hide很容易出现的问题就是承载Fragmen的Activity在旋转屏幕,资源不足被释放资源然后恢复重建这些情况下,被承载的Fragment在恢复时没能正确恢复状态,然后全部都显示了出来。解决这个问题的核心就是在Fragment恢复时全部隐藏,然后根据当前显示的Fragment的TAG显示Fragment。

本来想把这段代码封装成一个Activity基类,后面想想应用一级的需求页面千奇百怪,封装了也没什么通用性,就在这里记录一下,分享一下顺便备忘了=。=

上代码:

public abstract class MainActivity extends BaseActivity {
 
    private static final String SAVED_CURRENT_ID = "currentId";
 
    public static final List<String> PAGE_TAGS = new ArrayList<>();
    private List<Class<? extends BaseFragment>> fragmentClasses = Arrays.asList(HomeFragment.class,
            MoreFragment.class, ProfileFragment.class); //自定义的Fragment,主要目的是在初始化时能够通过循环初始化,与重建时的恢复统一
    private List<Fragment> fragments = new ArrayList<>();
 
    private FragmentManager mFragmentManager;
 
    private int currentId = 0;
 
    @Override
    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
        setContentView(R.layout.activity_main);
        initFragments();
    }
 
    private void initFragments(Bundle savedInstanceState) {
        mFragmentManager = getSupportFragmentManager();
        for(int i = 0; i < fragments.size(); i++) {
            fragments.set(i, mFragmentManager.findFragmentByTag(PAGE_TAGS.get(i)));
            if(fragments.get(i) == null) {
                try {
                    fragments.set(i,fragmentClasses.get(i).newInstance());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //重点:先重置所有fragment的状态为隐藏,彻底解决重叠问题
            if(fragments.get(i).isAdded()) {
                mFragmentManager.beginTransaction()
                .hide(fragments.get(i))
                .commitAllowingStateLoss();
            }
        }
        if(savedInstanceState != null) {
            int cachedId = savedInstanceState.getInt(SAVED_CURRENT_ID, 0);
            if(cachedId >= 0 && cachedId <= 4) {
                currentId = cachedId;
            }
        }
        switchFragment(currentId, false);
    }
 
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(SAVED_CURRENT_ID, currentId);
    }
 
    private void switchFragment(int index) {
        switchFragment(index, true);
    }
 
    private void switchFragment(int index, boolean anim) {
        /* Fragment 切换 */
        FragmentTransaction transaction = mFragmentManager.beginTransaction();
        if(anim) {  //显示切换动画
            if(index > currentId) {
                transaction.setCustomAnimations(R.anim.slide_right_in, R.anim.slide_left_out);
            }
            else {
                transaction.setCustomAnimations(R.anim.slide_left_in, R.anim.slide_right_out);
            }
        }
        if(fragments.get(index).isAdded()) {
            transaction.hide(fragments.get(currentId));
            transaction.show(fragments.get(index));
        }
        else {
            transaction.hide(fragments.get(currentId));
            transaction.add(getFragmentContainerLayoutRes(), fragments.get(index), PAGE_TAGS.get(index));
            transaction.show(fragments.get(index));
        }
        transaction.setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        transaction.commitAllowingStateLoss();
        currentId = index;
    }
}