打造安卓 App 丝滑的操作体验--Fragment 深入使用和封装之道

4,522 阅读11分钟
原文链接: www.jianshu.com

简介

想让App有丝滑般的切换速度和顺畅的体验么?那就放开Activity,使用Fragment来展示UI页面吧

Github futurice/android-best-practices上列举了一些列安卓开发最佳实践建议,其中对Fragment使用做了一些陈述(传送门),我表示赞同。

最近项目需要新增了一个功能模块,我引入了一个Activity,多个fragment的方式来组织UI,这个过程有了一些收获。

对fragment的操作,我使用support v4包下的,FragmentManager和FragmentTransaction这两个类。

正文

如何启动一个Fragment

replace方式

这里我没有用Activity下的getSupportFragmentManager得到了FragmentManager来替换Fragment,而是选择使用fragment下的getChildFragmentManager()获取FragmentManager。

放在基类BasicActivity里,

/**
 * 替换当前Fragment里的某个FrameLayout布局
 * @param resId 被替换的布局ID
 * @param fragmentTab 新的Fragment名
 * @param arguments 传入新的Fragment的Bundle
 * @param isAddToBack 是否加入回退栈
 */
private void replaceOneFragment(@IdRes int resId, String fragmentTab, Bundle arguments, boolean isAddToBack) {
    int childrenFragmentContainerResID = ((BasicFragment) mCurrentFragment).getChildrenFragmentContainerResID();
    int layoutId = resId <= 0 ? childrenFragmentContainerResID : resId;

    if (layoutId == -1) {
        throw new IllegalStateException("You should overwrite getChildrenFragmentContainerResID from BasicFragment");
    }

    FragmentManager manager = mCurrentFragment.getChildFragmentManager();
    if (manager != null) {
        FragmentTransaction transaction = manager.beginTransaction();

        transaction
                .setCustomAnimations(R.anim.right_enter, R.anim.left_exit, R.anim.left_enter, R.anim.right_exit)
                .replace(layoutId, fragmentProvider(fragmentTab, arguments), fragmentTab);
        if (isAddToBack) {
            transaction.addToBackStack(fragmentTab);
        }

        transaction.commitAllowingStateLoss();
    }
}

说明:

  1. getChildrenFragmentContainerResID(),该方法在BasicFragment里,用来获取要替换的布局ID
  2. BasicActivity里,fragmentProvider(),Fragment提供者

add/show/hide显示Fragment(支持SingleTask启动Fragment)

    /**
 * 显示特定Tag的Fragment,如果是第一次显示,则新建并添加该Fragment
 *
 * @param fragmentTab    Fragment标签名
 * @param arguments      传入Fragment的参数
 * @param isAddBackStack 是否加入FragmentManager回退栈
 * @param launchMode     启动模式 分为: STANDARD,SINGLE,SINGLE_ENHANCEMENT
 */
private void showOneFragment(String fragmentTab, Bundle arguments, boolean isAddBackStack, LaunchMode launchMode) {
    FragmentManager manager = getSupportFragmentManager();
    if (manager == null) {
        return;
    }

    Fragment fragmentByTag = manager.findFragmentByTag(fragmentTab);

    if (fragmentByTag != null && launchMode == LaunchMode.SINGLE_ENHANCEMENT) {
        popMultipleBackStack(fragmentTab, arguments);
        return;
    }

    FragmentTransaction transaction = manager.beginTransaction();
    //设置过渡动画
    transaction.setCustomAnimations(R.anim.right_enter, R.anim.left_exit, 0, 0);

    //隐藏当前所有fragment
    List<Fragment> fragments = manager.getFragments();
    if (fragments != null && fragments.size() > 0) {
        for (Fragment f : fragments) {
            if (f != null) {
                transaction.hide(f);
            }
        }
    }
    //第一次添加该Fragment
    if (fragmentByTag == null) {
        mCurrentFragment = fragmentProvider(fragmentTab, arguments);
        mFragmentBackDeque.push(fragmentTab);
        transaction.add(getFragmentContainerResID(), mCurrentFragment, fragmentTab);
        if (isAddBackStack) {
            transaction.addToBackStack(fragmentTab);
        }
        transaction.commitAllowingStateLoss();
        return;
    }

    if (!(fragmentByTag instanceof BasicFragment)) {
        throw new ClassCastException("fragment must extends BasicFragment");
    }

    //更新Arguments,按后退键时Fragment里的后退方法里使用
    if (arguments != null) {
        setSupportBackStackArguments(arguments);
    }

    //根据启动模式类型,采取不同的方式维护后退栈
    switch (launchMode) {
        case STANDARD:
            mFragmentBackDeque.push(fragmentTab);
            break;
        case SINGLE:
            synchronizeFragmentBackDequeWhenSingleLaunchMode(fragmentTab);
            break;
    }

    BasicFragment basicFragment = (BasicFragment) fragmentByTag;
    mCurrentFragment = fragmentByTag;
    basicFragment.setSupportArguments(arguments);
    transaction.show(fragmentByTag);
    transaction.commitAllowingStateLoss();
}

 /**
 * fragment 启动模式
 */
public enum LaunchMode {
    /**
     * 标准模式
     */
    STANDARD,
    /**
     * 单例模式,其他Fragment从自维护的mFragmentBackDeque栈里退出
     */
    SINGLE,
    /**
     * 强化版单例模式,其他Fragment从FragmentManager栈和自维护的mFragmentBackDeque栈里退出
     */
    SINGLE_ENHANCEMENT,
}

说明:

  1. popMultipleBackStack()实现一次弹出多个Fragment
  2. 在隐藏当前所有fragment操作,特别需要在遍历时,做个非空判断,

     for (Fragment f : fragments) {
             if (f != null) {
                 transaction.hide(f);
             }
        }

这样做,是因为Fragment出栈后,会出现栈内顺序不正确的bug,详看Fragment全解析系列(一):那些年踩过的坑,一文中关于多个Fragment同时出栈的深坑BUG这一部分的内容。

  1. synchronizeFragmentBackDequeWhenSingleLaunchMode()单例模式下,管理自维护的Fragment后退栈
  2. mFragmentBackDeque是自维护回退管理队列

后退键监听管理

使用Fragment组织UI后,返回上一个页面的逻辑有了变化。如果遇到之前replace替换了,则先从该fragment的FragmentManager里恢复原来的被替换的fragment,没有,则把之前hide状态的Fragment重新show显示出来,这个过程需要用了队列自己来维护回退

 @Override
public void onBackPressed() {
    if (mFragmentBackDeque == null || mCurrentFragment == null) {
        return;
    }

    //检查当前Fragment的ChildFragmentManager回退栈是否需要回退
    int childStackEntryCount = mCurrentFragment.getChildFragmentManager().getBackStackEntryCount();
    if (childStackEntryCount > 0) {
        mCurrentFragment.getChildFragmentManager().popBackStackImmediate();
        return;
    }

    //检查当前Fragment的自维护的回退栈是否需要回退
    if (mFragmentBackDeque.size() >= 2) {
        showOneFragmentOnBackPressed();
        return;
    }

    finish();
}
  1. showOneFragmentOnBackPressed()实现返回键显示特定Tag的Fragment

  2. 同时,如果需要,我们可以给FragmentManager添加OnBackStackChangedListener,监听FragmentManager回退栈成员数量的变化,具体使用见文末的代码

  3. 还有一种思路,View提供了setOnKeyListener(OnKeyListener onKeyListener),用OnKeyListener来监听后退健。

Fragment入参管理

第一次启动Fragment,走的是fragment生命周期方法,之后启动fragment从hide状态重新show时,不走Fragment生命周期方法,而是调用onHiddenChanged(boolean hidden)方法。因此,在这两种场景下支持给fragment传入参数,并且做到每次显示fragment,都能拿到最新的入参bundle。

我在BasicFragment设计如下:

/**
 * Fragment Argument解析
 * @param arguments
 */
protected void parseArguments(Bundle arguments){

}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    parseArguments(getArguments());
}

@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    if(!hidden){
        parseArguments(getSupportArguments());
    }
}

这样,新的Fragmnet在继承BasicFragment时,只需要重新parseArguments()即可

Toolbar管理

项目中使用Toolbar,用BasicFragmentWithToolbar来负责Toolbar的设置逻辑

BasicFragmentWithToolbar类里的核心代码

@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    if (!hidden) {
        configureToolbar();
    }
}

@Override
protected void configureToolbar() {
    super.configureToolbar();
    int stackEntryCount = getChildFragmentManager().getBackStackEntryCount();
    if (stackEntryCount > 0) {
        FragmentManager.BackStackEntry stackEntry = getChildFragmentManager().getBackStackEntryAt(stackEntryCount - 1);
        //Get the name that was supplied to FragmentTransaction.addToBackStack(String) when creating this entry
        String fragmentTab = stackEntry.getName();
        BasicFragmentWithToolbar fragmentByTag = (BasicFragmentWithToolbar) getChildFragmentManager().findFragmentByTag(fragmentTab);
        if (fragmentByTag != null) {
            fragmentByTag.setupToolbar();
        }
    } else {
        setupToolbar();
    }
}

/**
 * 设置Toolbar的显示内容
 */
protected void setupToolbar() {
    mToolbar.setNavigationIcon(R.drawable.icon_header_left);
    mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            getAttachActivity().onBackPressed();
        }
    });
}

如何使用上述的Fragment封装

我举个例子,我建一个BillContainerActivity,来作为Bill相关的Fragment容器,代码如下

public class BillContainerActivity extends BasicActivity {

private static final String TAG = "BillContainerActivity";

@Bind(R.id.frame_activity_order_body)
FrameLayout mOrderBody;


public static Intent getCallingIntent(Activity activity){
    Intent intent = new Intent(activity,BillContainerActivity.class);
    return intent;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    initializeShow();
}

private void initializeShow() {
    showOneFragment(BillHomeFragment.class.getSimpleName(),true);
}


@Override
protected BasicFragment fragmentProvider(String fragmentTab, Bundle arguments) {
    BasicFragment currentFragment;

    if(BillHomeFragment.class.getSimpleName().equals(fragmentTab)){
        currentFragment = BillHomeFragment.newInstance();
    }
    else if(BillProcessFragment.class.getSimpleName().equals(fragmentTab)){
        currentFragment = BillProcessFragment.newInstance(arguments);
    }
    else if(BillStructureFragment.class.getSimpleName().equals(fragmentTab)){
        currentFragment = BillStructureFragment.newInstance(arguments);
    }
    else if(AdjustBillDetailFragment.class.getSimpleName().equals(fragmentTab)){
        currentFragment = AdjustBillDetailFragment.newInstance(arguments);
    }
    else if(BrokerageDetailFragment.class.getSimpleName().equals(fragmentTab)){
        currentFragment = BrokerageDetailFragment.newInstance(arguments);
    }
    else if(InvoiceDetailFragment.class.getSimpleName().equals(fragmentTab)){
        currentFragment = InvoiceDetailFragment.newInstance(arguments);
    }
    else if(InvoiceListFragment.class.getSimpleName().equals(fragmentTab)){
        currentFragment = InvoiceListFragment.newInstance(arguments);
    }
    else{
        currentFragment = BillHomeFragment.newInstance();
    }
    return currentFragment;
}


@Override
protected int getFragmentContainerResID() {
    return R.id.frame_activity_order_body;
}

@Override
protected int getLayoutResID() {
    return R.layout.activity_bill_home;
}

}

说明
会发现作为Fragment容器的BillContainerActivity,代码量很少,主要做两件事

  1. 初始化显示Fragment
  2. 实现fragmentProvider方法,该方法把这个容器所需的Fragment构造出来

小结

再次使用Fragment,有了更深入的理解。代码不是很多就不放在github上,直接贴出来了,欢迎交流。

完整代码展示:

BasicFragment

/**
 * 
 * 1.绑定视图  2.RxBus订阅管理  3.Fragment回退栈回调管理      4.危险权限适配
 * 5.bundle解析
 */
public abstract class BasicFragment extends Fragment {

private BasicActivity mActivity;
private int mLastChildStackEntryCount = 0;
private int mLastSupportStackEntryCount = 0;

/**
 * hide后,再次show一个Fragment时传入的bundle
 */
private Bundle mSupportArguments;
/**
 * Subscription对象的容器
 */
private CompositeSubscription mCompositeSubscription;
/**
 * 防止fragment onDetach后调用getActivity空指针,不足:有可能内存泄漏
 * @param context
 */
@Override
public void onAttach(Context context) {
    super.onAttach(context);
    mActivity = (BasicActivity)context;
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    parseArguments(getArguments());
    setHasOptionsMenu(true);

    getChildFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            int currentStackEntryCount = getChildFragmentManager().getBackStackEntryCount();
            if(currentStackEntryCount - mLastChildStackEntryCount < 0){
                configureToolbar();
                onChildBackPressed();
            }
            mLastChildStackEntryCount = currentStackEntryCount;
        }
    });

    final FragmentManager manager = mActivity.getSupportFragmentManager();
    manager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            int currentStackEntryCount = manager.getBackStackEntryCount();
            if(currentStackEntryCount - mLastSupportStackEntryCount < 0){
                onSupportBackPressed(getAttachActivity().getSupportBackStackArguments());
            }
            mLastSupportStackEntryCount = currentStackEntryCount;
        }
    });
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = LayoutInflater.from(getContext()).inflate(getLayoutResID(),container,false);
    ButterKnife.bind(this,view);
    return view;
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
}

@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    if(!hidden){
        parseArguments(getSupportArguments());
    }
}

/**
 * Fragment Argument解析
 * @param arguments
 */
protected void parseArguments(Bundle arguments){

}


@Override
public void onDestroyView() {
    super.onDestroyView();
    ButterKnife.unbind(this);
}

@Override
public void onDestroy() {
    if(mCompositeSubscription != null){
        mCompositeSubscription.unsubscribe();
    }
    super.onDestroy();
}


private CompositeSubscription getCompositeSubscription(){
    if(mCompositeSubscription == null){
        mCompositeSubscription = new CompositeSubscription();
    }
    return mCompositeSubscription;
}

/**
 * 添加RxBus订阅
 * @param tClass
 * @param action1
 * @param errorAction1
 * @param <T>
 */
protected <T> void addSubscription(Class<T> tClass, Action1<T> action1, Action1<Throwable> errorAction1){
    getCompositeSubscription()
            .add(RxBus.getInstance().toSubscription(tClass, action1,errorAction1));
}

public BasicActivity getAttachActivity(){
    return mActivity;
}


private Bundle getSupportArguments() {
    return mSupportArguments;
}

public void setSupportArguments(Bundle supportArguments) {
    mSupportArguments = supportArguments;
}

/**
 * 当replace的置换Fragment时,重新show时,需要更新toolbar信息
 */
protected void configureToolbar() {
}

/**
 * 子Fragment回退栈 回调
 */
protected void onChildBackPressed(){

}

/**
 * Fragment里监听虚拟按键和实体按键的返回事件
 * @param bundle
 */
protected void onSupportBackPressed(Bundle bundle){

}

/**
 * 获取要替换的布局ID
 *
 * @return 替换的布局ID
 */
protected int getChildrenFragmentContainerResID() {
    return -1;
}

protected abstract @LayoutRes int getLayoutResID();


@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    MPermissions.onRequestPermissionsResult(this,requestCode,permissions,grantResults);
}

}

BasicFragmentWithToolbar

public abstract class BasicFragmentWithToolbar extends BasicFragment {

private static final String TAG = "BasicFragmentWithToolbar";

@Bind(R.id.toolbar_include)
Toolbar mToolbar;

@Bind(R.id.tv_include_header_title)
TextView mTvHeaderTitle;


@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = super.onCreateView(inflater, container, savedInstanceState);
    initToolbar();
    setupToolbar();
    adaptStatusBar();
    return view;
}

/**
 * Android 4.4时,设置状态栏透明,来适配标题栏样式
 */
private void adaptStatusBar() {
    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
        if (mToolbar != null) {
            WindowManager.LayoutParams localLayoutParams = getAttachActivity().getWindow().getAttributes();
            localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);

            int toolbarHeight = (int)getResources().getDimension(R.dimen.tool_bar_height_adapt);
            ViewGroup.LayoutParams layoutParams = mToolbar.getLayoutParams();
            layoutParams.height = toolbarHeight;

            int toolbarPaddingTop = (int) getResources().getDimension(R.dimen.tool_bar_padding_top);
            mToolbar.setPadding(0, toolbarPaddingTop, 0, 0);
        }
    }
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
}

@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    if (!hidden) {
        configureToolbar();
    }
}

/**
 * 初始化设置toolbar
 */
private void initToolbar() {
    mToolbar.setTitle("");
    mToolbar.setSubtitle("");
    getAttachActivity().setSupportActionBar(mToolbar);
}

@Override
protected void configureToolbar() {
    super.configureToolbar();
    int stackEntryCount = getChildFragmentManager().getBackStackEntryCount();
    if (stackEntryCount > 0) {
        FragmentManager.BackStackEntry stackEntry = getChildFragmentManager().getBackStackEntryAt(stackEntryCount - 1);
        //Get the name that was supplied to FragmentTransaction.addToBackStack(String) when creating this entry
        String fragmentTab = stackEntry.getName();
        BasicFragmentWithToolbar fragmentByTag = (BasicFragmentWithToolbar) getChildFragmentManager().findFragmentByTag(fragmentTab);
        if (fragmentByTag != null) {
            fragmentByTag.setupToolbar();
        }
    } else {
        setupToolbar();
    }
}

/**
 * 设置Toolbar的显示内容
 */
protected void setupToolbar() {
    mToolbar.setNavigationIcon(R.drawable.icon_header_left);
    mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            getAttachActivity().onBackPressed();
        }
    });
}

/**
 * 隐藏toolbar
 */
public void hideToolbar() {
    mToolbar.setVisibility(View.GONE);
}

public void setToolbarVisibilty(int visibilty) {
    mToolbar.setVisibility(visibilty);
}


public void setHeaderTitle(String titleContent) {
    mTvHeaderTitle.setText(titleContent);
}

public void setHeaderTitle(@StringRes int titleResID) {
    mTvHeaderTitle.setText(titleResID);
}

public void setNavigationIcon(@DrawableRes int resID) {
    mToolbar.setNavigationIcon(resID);
}

public void setupToolbar(@DrawableRes int leftResID, String titleContent) {
    mToolbar.setNavigationIcon(leftResID);
    setHeaderTitle(titleContent);
}

public Toolbar getToolbar() {
    return mToolbar;
}


protected abstract
@LayoutRes
int getLayoutResID();


}

BasicActivity

public abstract class BasicActivity extends AppCompatActivity {


private static final String TAG = "BasicActivity";

/**
 * Fragment回退管理的队列
 */
private Deque<String> mFragmentBackDeque = new ArrayDeque<>();
/**
 * 当前显示的Fragment
 */
private Fragment mCurrentFragment;

/**
 * 调用popBackStack系列方法,可通过设置此变量实现通信,为onSupportBackPressed()入参
 */
private Bundle mSupportBackStackArguments;
private Handler mHandler;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(getLayoutResID());
    ButterKnife.bind(this);
}

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


public void replaceOneFragment(String fragmentTab) {
    replaceOneFragment(0, fragmentTab, null, true);
}

public void replaceOneFragment(String fragmentTab, Bundle bundle) {
    replaceOneFragment(0, fragmentTab, bundle, true);
}

public void replaceOneFragment(@IdRes int resId, String fragmentTab) {
    replaceOneFragment(resId, fragmentTab, null, true);
}

public void replaceOneFragment(@IdRes int resId, String fragmentTab, Bundle bundle) {
    replaceOneFragment(resId, fragmentTab, bundle, true);
}

public void replaceOneFragment(String fragmentTab, boolean isAddToBack) {
    replaceOneFragment(0, fragmentTab, null, isAddToBack);
}

public void replaceOneFragment(@IdRes int resId, String fragmentTab, boolean isAddToBack) {
    replaceOneFragment(resId, fragmentTab, null, isAddToBack);
}

public void replaceOneFragment(String fragmentTab, Bundle bundle, boolean isAddToBack) {
    replaceOneFragment(0, fragmentTab, bundle, isAddToBack);
}

/**
 * 替换当前Fragment里的某个FrameLayout布局
 * @param resId 被替换的布局ID
 * @param fragmentTab 新的Fragment名
 * @param arguments 传入新的Fragment的Bundle
 * @param isAddToBack 是否加入回退栈
 */
private void replaceOneFragment(@IdRes int resId, String fragmentTab, Bundle arguments, boolean isAddToBack) {
    int childrenFragmentContainerResID = ((BasicFragment) mCurrentFragment).getChildrenFragmentContainerResID();
    int layoutId = resId <= 0 ? childrenFragmentContainerResID : resId;

    if (layoutId == -1) {
        throw new IllegalStateException("You should overwrite getChildrenFragmentContainerResID from BasicFragment");
    }

    FragmentManager manager = mCurrentFragment.getChildFragmentManager();
    if (manager != null) {
        FragmentTransaction transaction = manager.beginTransaction();

        transaction
                .setCustomAnimations(R.anim.right_enter, R.anim.left_exit, R.anim.left_enter, R.anim.right_exit)
                .replace(layoutId, fragmentProvider(fragmentTab, arguments), fragmentTab);
        if (isAddToBack) {
            transaction.addToBackStack(fragmentTab);
        }

        transaction.commitAllowingStateLoss();
    }
}


/**
 * 显示特定Tag的Fragment,如果是第一次显示,则新建并添加该Fragment
 *
 * @param fragmentTab
 */
public void showOneFragment(String fragmentTab) {
    showOneFragment(fragmentTab, null, true, STANDARD);
}

/**
 * 显示特定Tag的Fragment,如果是第一次显示,则新建并添加该Fragment
 *
 * @param fragmentTab
 * @param isAddToStack 第一次显示时,是否加入回退栈
 */
public void showOneFragment(String fragmentTab, boolean isAddToStack) {
    showOneFragment(fragmentTab, null, isAddToStack, STANDARD);
}


/**
 * 显示特定Tag的Fragment,如果是第一次显示,则新建并添加该Fragment
 *
 * @param fragmentTab
 * @param arguments
 */
public void showOneFragment(String fragmentTab, Bundle arguments) {
    showOneFragment(fragmentTab, arguments, true, STANDARD);
}

public void showOneFragment(String fragmentTab, Bundle arguments, LaunchMode launchMode) {
    showOneFragment(fragmentTab, arguments, true, launchMode);
}

/**
 * 显示特定Tag的Fragment,如果是第一次显示,则新建并添加该Fragment
 *
 * @param fragmentTab    Fragment标签名
 * @param arguments      传入Fragment的参数Bundle
 * @param isAddBackStack 是否加入FragmentManager回退栈
 * @param launchMode     启动模式 分为: STANDARD,SINGLE,SINGLE_ENHANCEMENT
 */
private void showOneFragment(String fragmentTab, Bundle arguments, boolean isAddBackStack, LaunchMode launchMode) {
    FragmentManager manager = getSupportFragmentManager();
    if (manager == null) {
        return;
    }

    Fragment fragmentByTag = manager.findFragmentByTag(fragmentTab);

    if (fragmentByTag != null && launchMode == LaunchMode.SINGLE_ENHANCEMENT) {
        popMultipleBackStack(fragmentTab, arguments);
        return;
    }

    FragmentTransaction transaction = manager.beginTransaction();
    //设置过渡动画
    transaction.setCustomAnimations(R.anim.right_enter, R.anim.left_exit, 0, 0);

    //隐藏当前所有fragment
    List<Fragment> fragments = manager.getFragments();
    if (fragments != null && fragments.size() > 0) {
        for (Fragment f : fragments) {
            if (f != null) {
                transaction.hide(f);
            }
        }
    }
    //第一次添加该Fragment
    if (fragmentByTag == null) {
        mCurrentFragment = fragmentProvider(fragmentTab, arguments);
        mFragmentBackDeque.push(fragmentTab);
        transaction.add(getFragmentContainerResID(), mCurrentFragment, fragmentTab);
        if (isAddBackStack) {
            transaction.addToBackStack(fragmentTab);
        }
        transaction.commitAllowingStateLoss();
        return;
    }

    if (!(fragmentByTag instanceof BasicFragment)) {
        throw new ClassCastException("fragment must extends BasicFragment");
    }

    //更新Arguments,按后退键时Fragment里的后退方法里使用
    if (arguments != null) {
        setSupportBackStackArguments(arguments);
    }

    //根据启动模式类型,采取不同的方式维护后退栈
    switch (launchMode) {
        case STANDARD:
            mFragmentBackDeque.push(fragmentTab);
            break;
        case SINGLE:
            synchronizeFragmentBackDequeWhenSingleLaunchMode(fragmentTab);
            break;
    }

    BasicFragment basicFragment = (BasicFragment) fragmentByTag;
    mCurrentFragment = fragmentByTag;
    basicFragment.setSupportArguments(arguments);
    transaction.show(fragmentByTag);
    transaction.commitAllowingStateLoss();
}


/**
 * 获取要替换的布局ID
 *
 * @return fragment替换的布局ID
 */
protected int getFragmentContainerResID() {
    return -1;
}

/**
 * 提供fragment
 * @param fragmentTab Fragment标签
 * @param arguments 传入Fragment的参数
 * @return
 */
protected BasicFragment fragmentProvider(String fragmentTab, Bundle arguments) {
    return null;
}

/**
 * 返回键显示特定Tag的Fragment
 */
private void showOneFragmentOnBackPressed() {
    mFragmentBackDeque.pop();
    String fragmentTab = mFragmentBackDeque.peek();

    FragmentManager manager = getSupportFragmentManager();
    if (manager != null) {
        FragmentTransaction transaction = manager.beginTransaction();
        transaction.setCustomAnimations(R.anim.left_enter, R.anim.right_exit, 0, 0);

        List<Fragment> fragments = manager.getFragments();
        for (Fragment f : fragments) {
            if (f != null) {
                transaction.hide(f);
            }
        }
        Fragment fragmentByTag = manager.findFragmentByTag(fragmentTab);
        if (fragmentByTag != null) {
            mCurrentFragment = fragmentByTag;
            transaction.show(fragmentByTag);
        }

        transaction.commitAllowingStateLoss();
    }
}

/**
 * 一次弹出多个Fragment
 *
 * @param tag
 * @param popFlag 0 弹出不包括tag所指的Fragment;1 表示弹出包括当前tag的fragment
 * @param bundle  回退栈 传输参数
 */
public void popMultipleBackStack(String tag, int popFlag, Bundle bundle) {
    if (bundle != null) {
        setSupportBackStackArguments(bundle);
    }
    //维护后退栈内容,保持同步
    if (mFragmentBackDeque.contains(tag)) {
        String peekElement = mFragmentBackDeque.peek();
        while (!tag.equals(peekElement)) {
            if (mFragmentBackDeque.isEmpty()) {
                break;
            }
            mFragmentBackDeque.pop();
            peekElement = mFragmentBackDeque.peek();
        }

        if (popFlag == 1) {
            if (!mFragmentBackDeque.isEmpty()) {
                mFragmentBackDeque.pop();
            }
        }
    }

    FragmentManager manager = getSupportFragmentManager();
    manager.popBackStackImmediate(tag, popFlag);
}

/**
 * 一次弹出多个Fragment
 *
 * @param tag
 * @param bundle 回退栈 传输参数
 */
private void popMultipleBackStack(String tag, Bundle bundle) {
    if (bundle != null) {
        setSupportBackStackArguments(bundle);
    }
    synchronizeFragmentBackDequeWhenSingleLaunchMode(tag);

    FragmentManager manager = getSupportFragmentManager();
    manager.popBackStackImmediate(tag, 0);

    reorderAvailIndicesToFixBug();
}

/**
 * 修复Fragment出栈后,栈内顺序不正确的bug
 */
private void reorderAvailIndicesToFixBug(){
    if(mHandler == null) {
        mHandler = new Handler(getMainLooper());
    }
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            FragmentTransactionBugFixHack.reorderIndices(getSupportFragmentManager());
        }
    });
}

/**
 * 单例模式下,管理自维护的Fragment后退栈
 *
 * @param tag
 */
private void synchronizeFragmentBackDequeWhenSingleLaunchMode(String tag) {
    if (mFragmentBackDeque.contains(tag)) {
        String peekElement = mFragmentBackDeque.peek();
        while (!tag.equals(peekElement)) {
            if (mFragmentBackDeque.isEmpty()) {
                break;
            }
            mFragmentBackDeque.pop();
            peekElement = mFragmentBackDeque.peek();
        }
    }
}

@Override
public void onBackPressed() {
    if (mFragmentBackDeque == null || mCurrentFragment == null) {
        return;
    }

    //检查当前Fragment的ChildFragmentManager回退栈是否需要回退
    int childStackEntryCount = mCurrentFragment.getChildFragmentManager().getBackStackEntryCount();
    if (childStackEntryCount > 0) {
        mCurrentFragment.getChildFragmentManager().popBackStackImmediate();
        return;
    }

    //检查当前Fragment的自维护的回退栈是否需要回退
    if (mFragmentBackDeque.size() >= 2) {
        showOneFragmentOnBackPressed();
        return;
    }

    finish();
}

public Fragment getCurrentFragment() {
    return mCurrentFragment;
}

public Deque<String> getFragmentBackDeque() {
    return mFragmentBackDeque;
}

public Bundle getSupportBackStackArguments() {
    if (mSupportBackStackArguments == null) {
        mSupportBackStackArguments = new Bundle();
    }
    return mSupportBackStackArguments;
}

public void setSupportBackStackArguments(Bundle supportBackStackArguments) {
    this.mSupportBackStackArguments = supportBackStackArguments;
}


protected abstract
@LayoutRes
int getLayoutResID();


/**
 * fragment 启动模式
 */
public enum LaunchMode {
    /**
     * 标准模式
     */
    STANDARD,
    /**
     * 单例模式,其他Fragment从自维护的mFragmentBackDeque栈里退出
     */
    SINGLE,
    /**
     * 强化版单例模式,其他Fragment从FragmentManager栈和自维护的mFragmentBackDeque栈里退出
     */
    SINGLE_ENHANCEMENT,
}
}

相关阅读资料

特别鸣谢:YoKey系列

  1. Fragment全解析系列(一):那些年踩过的坑
  2. Fragment全解析系列(二):正确的使用姿势
  3. Fragment之我的解决方案:Fragmentation

Android开发之Fragment最佳实践

What the Fragment?-Google I/O 2016(需自备梯子)

知乎:关于 Android,用多个 activity,还是单 activity 配合 fragment?

Android multiple fragment transaction ordering