前言

上一篇文章中详细分析了Fragment相关知识,那么作为“小Activity”,Fragment能做什么呢,如何使用Fragment得到最佳实践呢。Fragment的设计最初也许是为了大屏幕平板设备的需求,不过现在Fragment已经广泛运用到我们普通的手机设备上。下图是我们几乎在主流App中都能发现的一个功能。

熟悉Android的朋友一定都会知道,很简单嘛,使用TabHost就OK了!但是殊不知,TabHost并非是那么的简单,它的可扩展性非常的差,不能随意地定制Tab项显示的内容,而且运行还要依赖于ActivityGroup。ActivityGroup原本主要是用于为每一个TabHost的子项管理一个单独的Activity,但目前已经被废弃了。为什么呢?当然就是因为Fragment的出现了!

先创建宿主Activity

新建BestFragmentActivity

public class BestFragmentActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_best_fragment);//下面是LuseenBottomNavigation的使用BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigation);BottomNavigationItem bottomNavigationItem = new BottomNavigationItem("首页", ContextCompat.getColor(this, R.color.firstColor), R.mipmap.ic_account_balance_white_48dp);BottomNavigationItem bottomNavigationItem1 = new BottomNavigationItem("分类", ContextCompat.getColor(this, R.color.secondColor), R.mipmap.ic_list_white_48dp);BottomNavigationItem bottomNavigationItem2 = new BottomNavigationItem("任务", ContextCompat.getColor(this, R.color.firstColor), R.mipmap.ic_add_circle_outline_white_48dp);BottomNavigationItem bottomNavigationItem3 = new BottomNavigationItem("购物车", ContextCompat.getColor(this, R.color.thirdColor), R.mipmap.ic_add_shopping_cart_white_48dp);BottomNavigationItem bottomNavigationItem4 = new BottomNavigationItem("我的", ContextCompat.getColor(this, R.color.colorAccent), R.mipmap.ic_account_box_white_48dp);bottomNavigationView.addTab(bottomNavigationItem);bottomNavigationView.addTab(bottomNavigationItem1);bottomNavigationView.addTab(bottomNavigationItem2);bottomNavigationView.addTab(bottomNavigationItem3);bottomNavigationView.addTab(bottomNavigationItem4);}}

对应的布局文件activity_best_fragment

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/main_content"android:fitsSystemWindows="true"><!--Fragment之后就动态的放在该布局文件下--><FrameLayoutandroid:id="@+id/frame_content"android:layout_width="match_parent"android:layout_height="match_parent"android:scrollbars="none"android:layout_above="@+id/bottomNavigation"/><!--关于底层布局我这里使用了Github上的开源项目--><com.luseen.luseenbottomnavigation.BottomNavigation.BottomNavigationViewandroid:id="@+id/bottomNavigation"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"app:bnv_colored_background="false"app:bnv_with_text="true"app:bnv_shadow="false"app:bnv_tablet="false"app:bnv_viewpager_slide="true"app:bnv_active_color="@color/colorPrimary"app:bnv_active_text_size="@dimen/bottom_navigation_text_size_active"app:bnv_inactive_text_size="@dimen/bottom_navigation_text_size_inactive"/></RelativeLayout>

关于底层布局我这里使用了Github上的开源项目LuseenBottomNavigation,该项目地址是https://github.com/armcha/LuseenBottomNavigation读者可自行查看

接着创建Fragment

目前Fragment作为演示使用,可以看到布局内容都非常简单,我这里只给出其中一个Fragment的创建过程和源码,项目完整源码可见文末的源码地址。

我们就拿第一个GoodsFragment举例把

public class GoodsFragment extends Fragment {private static String TAG= GoodsFragment.class.getSimpleName();@Overridepublic void onAttach(Context context) {super.onAttach(context);Log.d(TAG,"onAttach");}@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {Log.d(TAG,"onCreateView");View view = inflater.inflate(R.layout.fragment_goods, null);return view;}@Overridepublic void onViewCreated(View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);Log.d(TAG,"onViewCreated");}@Overridepublic void onActivityCreated(@Nullable Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);Log.d(TAG,"onActivityCreated");}@Overridepublic void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.d(TAG,"onCreate");}@Overridepublic void onStart() {super.onStart();Log.d(TAG,"onStart");}@Overridepublic void onResume() {super.onResume();Log.d(TAG,"onResume");}@Overridepublic void onPause() {super.onPause();Log.d(TAG,"onPause");}@Overridepublic void onStop() {super.onStop();Log.d(TAG,"onStop");}@Overridepublic void onDestroyView() {super.onDestroyView();Log.d(TAG,"onDestroyView");}@Overridepublic void onDestroy() {super.onDestroy();Log.d(TAG,"onDestroy");}@Overridepublic void onDetach() {super.onDetach();Log.d(TAG,"onDetach");}}

源码非常的简单,在onCreateView中加载布局文件,该布局文件也非常简单,仅仅定义了一个帧布局,在帧布局中包含了一个TextView

<?xml version="1.0" encoding="utf-8"?><FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Goods"android:textStyle="bold"android:textSize="30sp"android:layout_gravity="center"/></FrameLayout>

按照上面的流程我们建立了所需的Fragment,接着该更改BestFragmentActivity的代码,更改后的源码如下

public class BestFragmentActivity extends AppCompatActivity{@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_best_fragment);//底部导航布局BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigation);BottomNavigationItem bottomNavigationItem = new BottomNavigationItem("首页", ContextCompat.getColor(this, R.color.firstColor), R.mipmap.ic_account_balance_white_48dp);BottomNavigationItem bottomNavigationItem1 = new BottomNavigationItem("分类", ContextCompat.getColor(this, R.color.secondColor), R.mipmap.ic_list_white_48dp);BottomNavigationItem bottomNavigationItem2 = new BottomNavigationItem("任务", ContextCompat.getColor(this, R.color.firstColor), R.mipmap.ic_add_circle_outline_white_48dp);BottomNavigationItem bottomNavigationItem3 = new BottomNavigationItem("购物车", ContextCompat.getColor(this, R.color.thirdColor), R.mipmap.ic_add_shopping_cart_white_48dp);BottomNavigationItem bottomNavigationItem4 = new BottomNavigationItem("我的", ContextCompat.getColor(this, R.color.colorAccent), R.mipmap.ic_account_box_white_48dp);bottomNavigationView.addTab(bottomNavigationItem);bottomNavigationView.addTab(bottomNavigationItem1);bottomNavigationView.addTab(bottomNavigationItem2);bottomNavigationView.addTab(bottomNavigationItem3);bottomNavigationView.addTab(bottomNavigationItem4);//为底部导航布局设置点击事件bottomNavigationView.setOnBottomNavigationItemClickListener(new OnBottomNavigationItemClickListener() {@Overridepublic void onNavigationItemClick(int i) {switch (i){case 0:switchToHome();break;case 1:switchToCategory();break;case 2:switchToTask();break;case 3:switchToGoodCar();break;case 4:switchToAbout();break;}}});//初始加载首页,即GoodsFragmentswitchToHome();}private void switchToAbout() {getSupportFragmentManager().beginTransaction().replace(R.id.frame_content,new AboutFragment(),AboutFragment.class.getName()).commit();}private void switchToCategory() {getSupportFragmentManager().beginTransaction().replace(R.id.frame_content,new CategoryFragment(),CategoryFragment.class.getName()).commit();}private void switchToTask() {getSupportFragmentManager().beginTransaction().replace(R.id.frame_content,new TaskFragment(),TaskFragment.class.getName()).commit();}private void switchToGoodCar() {getSupportFragmentManager().beginTransaction().replace(R.id.frame_content,new GoodCarFragment(),GoodCarFragment.class.getName()).commit();}private void switchToHome() {getSupportFragmentManager().beginTransaction().replace(R.id.frame_content,new GoodsFragment(),GoodsFragment.class.getName()).commit();}
}

上面的代码可以根据上一篇文章比较容易的写出来,而且正常运行,可是在实际开发过程中我们不得不考虑代码的性能问题。其实上面的代码存在性能问题,尤其是在底部导航这种场景中,Fragment之间的来回切换,这里使用的replace方法。关于这个方法带来的问题以及如何进行优化,将在下一节详细说明。

Fragment 性能优化问题

FragmentTransaction

谈到Fragment的性能优化问题,就不得不对FragmentTransaction进行深入的研究以及探讨,上面使用了getSupportFragmentManager().beginTransaction()得到了FragmentTransaction对象,并依次调用其replace方法和commit方法。

  • replace(int containerViewId, Fragment fragment)、replace(int containerViewId, Fragment fragment, String tag)

该方法的作用是,类似于先remove掉视图容器所有的Fragment,再add方法参数中的fragment,并为该Fragment设置标签tag。

getSupportFragmentManager().beginTransaction().add(R.id.frame_content,new AboutFragment(),AboutFragment.class.getName()).commit();getSupportFragmentManager().beginTransaction().add(R.id.frame_content,new CategoryFragment(),CategoryFragment.class.getName()).commit();getSupportFragmentManager().beginTransaction().add(R.id.frame_content,new TaskFragment(),TaskFragment.class.getName()).commit();getSupportFragmentManager().beginTransaction().replace(R.id.frame_content,new GoodsFragment(),GoodsFragment.class.getName()).commit();

如上面所示代码块中,我们先进行了3次添加操作,之后的replace操作会移出前面添加的Fragment,再添加方法参数中指定的Frament。

  • add(int containerViewId, Fragment fragment, String tag)、 remove(Fragment fragment)

FragmentTransaction的Add()操作是维持着一个队列的,在这个队列中,根据ADD进去的先后顺序形成了一个链表,我们上面的操作在这个列表中的形式变化如下图所示:

  • remove(Fragment fragment) : 移除一个已经存在的Fragment.
  • show(Fragment fragment): 显示一个以前被隐藏过的Fragment
  • hide(Fragment fragment) : 隐藏一个存在的Fragment

    注:①Fragment被hide/show,仅仅是隐藏/显示Fragment的视图,不会有任何生命周期方法的调用。

    ②在Fragment中重写onHiddenChanged方法可以对Fragment的hide和show状态进行监听。

还有一些其他的方法这里就不一一列举了,有了上面所列出的方法,我们就能对Fragment有个很不错的优化了。

Fragment性能问题分析与解决

Fragment性能问题分析

我们上面是使用replace来切换页面,那么在每次切换的时候,Fragment都会重新实例化,重新加载一边数据,这样非常消耗性能和用户的数据流量。这是因为replace操作,每次都会把container中的现有的fragment实例清空,然后再把指定的fragment添加进去,就就造成了在切换到以前的fragment时,就会重新实例会fragment。

Fragment性能问题解决

知道了问题的根源所在,那么解决的办法也呼之欲出了。我们不能使用replace来进行页面的切换,那么可使用的方法貌似只有add了,我们可以在加载的时候判断Fragment是不是已经被添加到队列中,如果已添加,我们就显示(show)该Fragment,隐藏(hide)其他,如果没有添加过呢,就添加。这样就能做到多个Fragment切换不重新实例化。具体到代码中就是这样的

public class BestFragmentActivity extends AppCompatActivity{//当前的Fragmentprivate Fragment mCurFragment = new Fragment();//初始化其他的Fragmentprivate GoodsFragment mGoodsFragment = new GoodsFragment();private GoodCarFragment mGoodCarFragment = new GoodCarFragment();private TaskFragment mTaskFragment = new TaskFragment();private AboutFragment mAboutFragment  = new AboutFragment();private CategoryFragment mCategoryFragment  = new CategoryFragment();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_best_fragment);BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigation);BottomNavigationItem bottomNavigationItem = new BottomNavigationItem("首页", ContextCompat.getColor(this, R.color.firstColor), R.mipmap.ic_account_balance_white_48dp);BottomNavigationItem bottomNavigationItem1 = new BottomNavigationItem("分类", ContextCompat.getColor(this, R.color.secondColor), R.mipmap.ic_list_white_48dp);BottomNavigationItem bottomNavigationItem2 = new BottomNavigationItem("任务", ContextCompat.getColor(this, R.color.firstColor), R.mipmap.ic_add_circle_outline_white_48dp);BottomNavigationItem bottomNavigationItem3 = new BottomNavigationItem("购物车", ContextCompat.getColor(this, R.color.thirdColor), R.mipmap.ic_add_shopping_cart_white_48dp);BottomNavigationItem bottomNavigationItem4 = new BottomNavigationItem("我的", ContextCompat.getColor(this, R.color.colorAccent), R.mipmap.ic_account_box_white_48dp);bottomNavigationView.addTab(bottomNavigationItem);bottomNavigationView.addTab(bottomNavigationItem1);bottomNavigationView.addTab(bottomNavigationItem2);bottomNavigationView.addTab(bottomNavigationItem3);bottomNavigationView.addTab(bottomNavigationItem4);bottomNavigationView.setOnBottomNavigationItemClickListener(new OnBottomNavigationItemClickListener() {@Overridepublic void onNavigationItemClick(int i) {switch (i){case 0:switchToHome();break;case 1:switchToCategory();break;case 2:switchToTask();break;case 3:switchToGoodCar();break;case 4:switchToAbout();break;}}});switchToHome();}private void switchFragment(Fragment targetFragment){FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();if (!targetFragment.isAdded()) {//如果要显示的targetFragment没有添加过transaction.hide(mCurFragment)//隐藏当前Fragment.add(R.id.frame_content, targetFragment,targetFragment.getClass().getName())//添加targetFragment.commit();} else {//如果要显示的targetFragment已经添加过transaction//隐藏当前Fragment.hide(mCurFragment).show(targetFragment)//显示targetFragment.commit();}//更新当前Fragment为targetFragmentmCurFragment = targetFragment;}private void switchToAbout() {switchFragment(mAboutFragment);}private void switchToCategory() {switchFragment(mCategoryFragment);}private void switchToTask() {switchFragment(mTaskFragment);}private void switchToGoodCar() {switchFragment(mGoodCarFragment);}private void switchToHome() {switchFragment(mGoodsFragment);}}

这样就达到了我们的目的,我们在来回切换的操作中,Fragment只实例一次,少了销毁又重新创建等带来的性能消耗,另我们想要在Fragment中更新数据时,我们可以在自定义Fragment中重写其onHiddenChanged方法

@Override
public void onHiddenChanged(boolean hidden) {super.onHiddenChanged(hidden);if (hidden){//Fragment隐藏时调用}else {//Fragment显示时调用}}

源码地址:源码传送门

本篇总结

我们在本篇博客中比较详细的给出了一个Fragment的最佳实践,我们在许多主流App中都能看到这种顶部、底部导航的效果,并且在此基础上我们探讨了使用Fragment不当的存在性能问题及优化。

Good luck!

Reprinted by Jimmy.li

Fragment最佳实践相关推荐

  1. 30 个 Python 的最佳实践、小贴士和技巧

    作者 | Erik-Jan van Baaren 译者 | 弯月,责编 | 屠敏 出品 | CSDN(ID:CSDNnews) 以下为译文: 元旦过完了,我们都纷纷回到了各自的工作岗位.新的一年新气象 ...

  2. 30个python的最佳实践,快去试试吧!

    30 个 Python 的最佳实践.小贴士和技巧 原:作者 | Erik-Jan van Baaren 原:译者 | 弯月,责编 | 屠敏 以下为译文: 1. Python 版本 在此想提醒各位:自2 ...

  3. JS最佳实践——红皮书

    最佳实践 前言 1 可维护性 2 降低耦合 2.1 将css从js中抽离 2.2 模板文本写注释 2.3 应用逻辑 / 事件处理程序分离 2.3.1 概念 2.3.2 Demo 2.4 松散耦合原则 ...

  4. 你不知道的 React 最佳实践

    React ⚛️ React 是一个用于开发用户界面的 JavaScript 库, 是由 Facebook 在 2013 年创建的. React 集成了许多令人兴奋的组件.库和框架[1]. 当然,开发 ...

  5. Android开发最佳实践

    原文链接:https://github.com/futurice/android-best-practices 转载来源:http://blog.csdn.net/asce1885/article/d ...

  6. App瘦身最佳实践(分析了微信、淘宝、微博图片文件的放法)

    本文会不定期更新,推荐watch下项目.如果喜欢请star,如果觉得有纰漏请提交issue,如果你有更好的点子可以提交pull request. 本文的示例代码主要是基于作者的经验来编写的,若你有其他 ...

  7. 碎片的最佳实践——一个简易版的新闻应用

    现在你已经将关于碎片的重要知识点都掌握得差不多了,不过在灵活运用方面可能还有些欠缺,因此又该进入最佳实践环节了. 前面有提到过,碎片很多时候都是在平板开发当中使用的,主要是为了解决屏幕空间不能充分利用 ...

  8. 《碎片的最佳实践》读书笔记

    转载请注明出处:http://blog.csdn.net/chengbao315/article/details/50962716 最近学习了郭霖<第一行代码>的4.5章节<碎片的最 ...

  9. App 瘦身最佳实践

    原文链接:www.jianshu.com Android本文会不定期更新,推荐watch下项目.如果喜欢请star,如果觉得有纰漏请提交issue,如果你有更好的点子可以提交pull request. ...

最新文章

  1. 到底能不能做一辈子的程序员——大龄程序员将何去何从
  2. 关于ASP.NET MVC的业务逻辑验证(validation)
  3. 【实施工程师】Wampserver64橙色无法启动【apache】问题
  4. 使用 FTP 迁移 SQL Server 数据_迁移数据_快速入门(SQL Server)_云数据库 RDS 版-阿里云...
  5. Spring MVC 3.0 RESTful controller
  6. wxpython窗口固定大小_调整wxPython窗口的大小
  7. BS结构浏览器网页读写IC卡技术汇总
  8. Navicat Premium 12.1.16.0 安装与激活(图文教程)
  9. ant man 什么意思,ant
  10. 第二重要极限公式推导过程_机器学习——一文详解逻辑回归「附详细推导和代码」...
  11. 必看,关于sql的慢查询及解决方案
  12. Java实现Native微信支付 超完整流程
  13. android 来电默认铃声,android – 来电动态覆盖默认铃声
  14. JAVA 编写一个员工类,成员变量和成员方法自拟,编写一个测试类
  15. 计算机硬盘越大运行速度越大吗,固态硬盘越大运行速度越快吗
  16. 请教 ANDROID 通信信号、网络信号图标的颜色问题
  17. Python编程:实现谢尔宾斯基三角
  18. 学习游戏建模的方式有哪些?次世代游戏建模学习路线以及要掌握的3D软件分别是?
  19. java模拟post请求上传图片
  20. 综述:边缘视频在公共安全领域的应用

热门文章

  1. Websocket协议原理及Ws服务器代码实现
  2. Power BI--DAX函数进阶
  3. 主席树经典应用区间合并
  4. java docx文件 合并 合并成一个大docx文件
  5. 微信小程序分享朋友圈生成海报
  6. (转)YOLO-V3可视化训练过程中的参数,绘制loss、IOU、avg Recall等的曲线图
  7. JAVA 上传图片时 压缩后上传
  8. 多线程并发执行及解决方法
  9. 太极熊猫服务器怎么都在维护,太极熊猫3月8日战区调整及维护公告
  10. 阮老师网络日志-记录