本文概述
- 活动世界
- 碎片世界
- Android中的全屏碎片梦m
- fragment导航模式
- 总结
我最喜欢的导航模式实现之一是基于” 一次活动多个fragment” 的哲学, 或者简称为fragment导航模式, 其中应用程序中的每个屏幕都是全屏fragment, 所有或大部分这些fragment都包含在其中。一个活动。
【Android开发人员的fragment导航模式指南】这种方法不仅简化了导航的实现方式, 而且具有更好的性能, 因此提供了更好的用户体验。
在本文中, 我们将介绍Android中一些常见的导航模式实现, 然后介绍基于Fragment的导航模式, 并与其他导航模式进行比较和对比。实现此模式的演示应用程序已上传到GitHub。
活动世界 一个典型的仅使用活动的Android应用程序被组织成一个树状结构(更确切地说是一个有向图), 其中根活动由启动器启动。当你在应用程序中导航时, 操作系统会维护一个活动后退堆栈。
下图显示了一个简单的示例:
文章图片
活动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。除了明显的设计缺陷之外, 这种方法还有另一个问题。看下面的图:
文章图片
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” 模式, 但我仍在使用它。关于此方法的讨论很多, 例如在这里和这里。但是我错过的是一个具体的示例, 我可以自己查看和测试。
让我们看下图:
文章图片
现在, 我们只有一个容器活动, 并且有多个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时才可以访问抽屉。图中虚线右边的所有内容均作为任意导航方案的示例。
文章图片
由于容器活动保存了抽屉, 因此我们只有一个抽屉实例, 因此在每个应该看到抽屉的导航步骤中, 你都无需充气并重新初始化它。仍不确信所有这些工作原理如何?看一下我的示例应用程序, 该示例程序演示了抽屉的用法。
缺点
我最大的担心一直是, 如果我在项目中使用基于fragment的导航模式, 那么在将来的某个地方, 我会遇到无法预料的问题, 因为fragment, 第三方库和不同的OS版本会增加复杂性, 因此很难解决。如果我不得不重构到目前为止所做的一切怎么办?
确实, 我必须解决嵌套fragment的问题, 第三方库也使用诸如ShinobiControls, ViewPagers和FragmentStatePagerAdapters之类的fragment。
我必须承认, 获得足够的碎片经验以解决这些问题是一个相当漫长的过程。但是, 在每种情况下, 问题都不是哲学不好, 而是我对fragment的理解不够。也许如果你比我更了解fragment, 你甚至都不会遇到这些问题。
我现在唯一提到的缺点是, 由于没有成熟的库可以展示基于fragment导航的复杂应用程序的所有复杂场景, 因此我们仍然会遇到无法解决的问题。
总结 在本文中, 我们看到了在Android应用程序中实现导航的另一种方法。我们将它与使用活动的传统导航哲学进行了比较, 并且我们已经看到了使用它优于传统方法的一些很好的理由。
如果你还没有, 请查看上传到GitHub实施的演示应用程序。随意分叉或通过更好的示例做出贡献, 以更好地展示其用法。
相关:Android开发人员最常犯的10个错误
推荐阅读
- 保持冷静并过渡到新的开发团队
- 未充分使用的Android库的详尽指南
- Ionic开发人员最常犯的9个错误
- 认识RxJava(缺少的Android反应式编程库)
- 优化Android应用的提示和工具
- Android获取IP地址
- cisp401系列PayrollApp.java版本
- 退出ANDROID应用程序AIR
- 禁用包含apple内容的网页=/