简介
想让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();
}
}
说明:
- getChildrenFragmentContainerResID(),该方法在BasicFragment里,用来获取要替换的布局ID
- 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,
}
说明:
- popMultipleBackStack()实现一次弹出多个Fragment
-
在隐藏当前所有fragment操作,特别需要在遍历时,做个非空判断,
for (Fragment f : fragments) { if (f != null) { transaction.hide(f); } }
这样做,是因为Fragment出栈后,会出现栈内顺序不正确的bug,详看Fragment全解析系列(一):那些年踩过的坑,一文中关于多个Fragment同时出栈的深坑BUG这一部分的内容。
- synchronizeFragmentBackDequeWhenSingleLaunchMode()单例模式下,管理自维护的Fragment后退栈
- 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();
}
-
showOneFragmentOnBackPressed()实现返回键显示特定Tag的Fragment
-
同时,如果需要,我们可以给FragmentManager添加OnBackStackChangedListener,监听FragmentManager回退栈成员数量的变化,具体使用见文末的代码
-
还有一种思路,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,代码量很少,主要做两件事
- 初始化显示Fragment
- 实现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系列
What the Fragment?-Google I/O 2016(需自备梯子)