Android开发人员的fragment导航模式指南

本文概述

  • 活动世界
  • 碎片世界
  • Android中的全屏碎片梦m
  • fragment导航模式
  • 总结
多年来, 我看到了Android中许多不同的导航模式实现。一些应用仅使用活动, 而其他应用则与fragment和/或自定义视图混合使用。
我最喜欢的导航模式实现之一是基于” 一次活动多个fragment” 的哲学, 或者简称为fragment导航模式, 其中应用程序中的每个屏幕都是全屏fragment, 所有或大部分这些fragment都包含在其中。一个活动。
【Android开发人员的fragment导航模式指南】这种方法不仅简化了导航的实现方式, 而且具有更好的性能, 因此提供了更好的用户体验。
在本文中, 我们将介绍Android中一些常见的导航模式实现, 然后介绍基于Fragment的导航模式, 并与其他导航模式进行比较和对比。实现此模式的演示应用程序已上传到GitHub。
活动世界 一个典型的仅使用活动的Android应用程序被组织成一个树状结构(更确切地说是一个有向图), 其中根活动由启动器启动。当你在应用程序中导航时, 操作系统会维护一个活动后退堆栈。
下图显示了一个简单的示例:
Android开发人员的fragment导航模式指南

文章图片
活动A1是我们应用程序中的入口点(例如, 它表示启动屏幕或主菜单), 用户可以从中导航到A2或A3。当你需要在活动之间进行通信时, 可以使用startActivityForResult(), 也可以在活动之间共享可全局访问的业务逻辑对象。
当你需要添加新的活动时, 需要执行以下步骤:
  • 定义新活动
  • 在AndroidManifest.xml中注册
  • 使用另一个活动的startActivity()将其打开
当然, 此导航图是一种相当简单的方法。当你需要操纵后退堆栈或必须多次重用同一活动时, 例如当你想在某些教程屏幕中导航用户, 但实际上每个屏幕都使用与基础。
幸运的是, 我们为它提供了称为任务的工具和一些适当的向后堆栈导航的准则。
然后, 在API级别11中出现了fragment…
碎片世界
Android在Android 3.0(API级别11)中引入了fragment, 主要是为了在大屏幕(例如平板电脑)上支持更动态和灵活的UI设计。由于平板电脑的屏幕比手机的屏幕大得多, 因此有更多的空间来组合和交换UI组件。fragment允许进行此类设计, 而无需你管理视图层次结构的复杂更改。通过将活动的布局分成多个fragment, 你可以在运行时修改活动的外观, 并将这些更改保存在由活动管理的后台堆栈中。 –摘自Google的Fragments API指南。
这个新玩具允许开发人员构建多窗格UI并在其他活动中重用组件。有些开发人员喜欢这个, 而其他开发人员则不喜欢。是否使用fragment是一个流行的争论, 但是我认为每个人都会同意, fragment带来了额外的复杂性, 开发人员确实需要理解它们才能正确使用它们。
Android中的全屏碎片梦m 我开始看到越来越多的示例, 其中fragment不仅代表屏幕的一部分, 而且实际上整个屏幕都是活动中包含的fragment。我什至看到一个设计, 其中每个活动都只有一个全屏fragment, 仅此而已, 而这些活动存在的唯一原因是托管这些fragment。除了明显的设计缺陷之外, 这种方法还有另一个问题。看下面的图:
Android开发人员的fragment导航模式指南

文章图片
A1如何与F1通信?自创建F1以来, A1井对F1拥有完全控制权。例如, A1可以在创建F1时传递捆绑, 也可以调用其公共方法。 F1如何与A1通信?嗯, 这更加复杂, 但是可以通过回调/观察者模式来解决, 其中A1订阅F1, 而F1通知A1。
但是, A1和A2如何相互通信?例如, 已经通过startActivityForResult()对此进行了介绍。
现在真正的问题来了:F1和F2如何相互通信?即使在这种情况下, 我们也可以拥有一个全球可用的业务逻辑组件, 因此可以用来传递数据。但这并不总是导致优雅的设计。如果F2需要以更直接的方式将一些数据传递给F1怎么办?好了, 使用回调模式F2可以通知A2, 然后A2完成结果, 并且该结果由A1捕获, 并通知F1。
这种方法需要大量样板代码, 并迅速成为错误, 痛苦和愤怒的源头。
如果我们可以摆脱所有活动并只保留其中一个保留其余部分, 该怎么办?
fragment导航模式 多年以来, 我开始在大多数应用程序中使用” 一次活动多个fragment” 模式, 但我仍在使用它。关于此方法的讨论很多, 例如在这里和这里。但是我错过的是一个具体的示例, 我可以自己查看和测试。
让我们看下图:
Android开发人员的fragment导航模式指南

文章图片
现在, 我们只有一个容器活动, 并且有多个fragment, 这些fragment又具有树状结构。它们之间的导航由FragmentManager处理, 它具有其后向堆栈。
请注意, 现在我们没有startActivityForResult(), 但是我们可以实现回调/观察者模式。让我们看看这种方法的优缺点:
优点:
1.更干净, 更可维护的AndroidManifest.xml 现在我们只有一个活动, 因此不再需要在每次添加新屏幕时都更新清单。与活动不同, 我们不必声明fragment。
这似乎是一件小事, 但是对于具有50多个活动的大型应用程序来说, 这可以显着提高AndroidManifest.xml文件的可读性。
查看具有几个屏幕的示例应用程序的清单文件。清单文件仍然非常简单。
< ?xml version="1.0" encoding="utf-8"?> package="com.exarlabs.android.fragmentnavigationdemo.ui" > < application android:name= ".FragmentNavigationDemoApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> < activity android:name="com.exarlabs.android.fragmentnavigationdemo.ui.MainActivity" android:label="@string/app_name" android:screenOrientation="portrait" android:theme="@style/AppTheme.NoActionBar" > < intent-filter> < action android:name="android.intent.action.MAIN" /> < category android:name="android.intent.category.LAUNCHER" /> < /intent-filter> < /activity> < /application> < /manifest>

2.集中导航管理 在我的代码示例中, 你将看到我使用了NavigationManager, 就我而言, 它被注入每个fragment中。该管理器可以用作日志记录, 后向堆栈管理等的集中场所, 因此导航行为与其余业务逻辑分离, 并且不会在不同屏幕的实现中散布开来。
假设有一种情况, 我们想要启动一个屏幕, 用户可以在该屏幕上从人员列表中选择一些项目。你还希望传递一些过滤参数, 例如年龄, 职业和性别。
如果是” 活动” , 则应输入:
Intent intent = new Intent(); intent.putExtra("age", 40); intent.putExtra("occupation", "developer"); intent.putExtra("gender", "female"); startActivityForResult(intent, 100);

然后, 你必须在下面的某个位置定义onActivityResult并处理结果。
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); }

我个人对此方法的问题是, 这些参数是” 引号” , 它们不是强制性的, 因此, 我必须确保在缺少额外内容时, 接收活动能够处理所有不同的情况。稍后, 当进行一些重构并且不再需要例如” 年龄” 额外的东西时, 那么我必须在启动此活动的代码中搜索各处, 并确保所有额外的东西都是正确的。
此外, 如果结果(人员列表)以_List _的形式而不是必须反序列化的序列化形式到达, 会不会更好?
在基于fragment的导航的情况下, 一切都更加简单。你要做的就是在NavigationManager中编写一个名为startPersonSelectorFragment()的方法, 该方法带有必要的参数和回调实现。
mNavigationManager.startPersonSelectorFragment(40, "developer", "female", new PersonSelectorFragment.OnPersonSelectedListener() { @Override public boolean onPersonsSelected(List< Person> selection) { [do something] return false; } });

或搭配RetroLambda
mNavigationManager.startPersonSelectorFragment(40, "developer", "female", selection -> [do something]);

3.屏幕之间更好的通讯方式 在活动之间, 我们只能共享可以保存原语或序列化数据的捆绑包。现在, 有了fragment, 我们可以实现一个回调模式, 例如, F1可以侦听F2传递任意对象。请查看前面的示例的回调实现, 该实现返回_List _。
4.建筑碎片比建筑活动便宜 当你使用一个有5个菜单项的抽屉时, 这一点变得很明显, 并且应该在每页上再次显示该抽屉。
在纯活动导航的情况下, 每个页面都应充气并初始化抽屉, 这当然很昂贵。
在下图中, 你可以看到几个根fragment(FR *), 它们是可以从抽屉直接访问的全屏fragment, 并且只有显示这些fragment时才可以访问抽屉。图中虚线右边的所有内容均作为任意导航方案的示例。
Android开发人员的fragment导航模式指南

文章图片
由于容器活动保存了抽屉, 因此我们只有一个抽屉实例, 因此在每个应该看到抽屉的导航步骤中, 你都无需充气并重新初始化它。仍不确信所有这些工作原理如何?看一下我的示例应用程序, 该示例程序演示了抽屉的用法。
缺点
我最大的担心一直是, 如果我在项目中使用基于fragment的导航模式, 那么在将来的某个地方, 我会遇到无法预料的问题, 因为fragment, 第三方库和不同的OS版本会增加复杂性, 因此很难解决。如果我不得不重构到目前为止所做的一切怎么办?
确实, 我必须解决嵌套fragment的问题, 第三方库也使用诸如ShinobiControls, ViewPagers和FragmentStatePagerAdapters之类的fragment。
我必须承认, 获得足够的碎片经验以解决这些问题是一个相当漫长的过程。但是, 在每种情况下, 问题都不是哲学不好, 而是我对fragment的理解不够。也许如果你比我更了解fragment, 你甚至都不会遇到这些问题。
我现在唯一提到的缺点是, 由于没有成熟的库可以展示基于fragment导航的复杂应用程序的所有复杂场景, 因此我们仍然会遇到无法解决的问题。
总结 在本文中, 我们看到了在Android应用程序中实现导航的另一种方法。我们将它与使用活动的传统导航哲学进行了比较, 并且我们已经看到了使用它优于传统方法的一些很好的理由。
如果你还没有, 请查看上传到GitHub实施的演示应用程序。随意分叉或通过更好的示例做出贡献, 以更好地展示其用法。
相关:Android开发人员最常犯的10个错误

    推荐阅读