神刀安全网

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


简介

想让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

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 打造安卓App丝滑的操作体验--Fragment深入使用和封装之道

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址