百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Android官方架构组件Navigation:Fragment管理框架

ccwgpt 2024-11-22 11:43 24 浏览 0 评论

?一. 背景

从项目发展来看,随着业务迭代,我们 Fragment 之间的跳转越来越多:

Android 客户端组件化之后,由于团队基础的架构单个的业务组件规约是一个Activity 当中包含着多个Fragment。因此随着业务的迭代Fragment会越来越多,Fragment 之间的跳转,按照现有的方法,是采用封装的ReplaceFragment 方法,会有大量的跳转逻辑埋藏在代码里面令人头疼,也严重不符合单一职责。需要一种新的方案来进行Fragment 之间的管理。

从 Android 发展来看,简单的 Intent 无法满足界面跳转需求:

从一个界面跳转到另一界面,这是安卓开发的基础部分。过去,你可以使用 Intent 交互来完成此操作,在简单的情况下,例如单击按钮,这很容易。但如果你想做一些稍微复杂的事情呢?例如,像底部导航这样常见模式。你需要确保不仅你的底部导航视图可以真的导航,而且还要突出显示正确的按钮。而且它以统一的方式处理后台堆叠,这样用户就不会失去方向或迷茫。像这样的案例是新导航组件闪耀的地方。

Navigation 的出现:

该导航组件是一个可简化安卓导航的插件和工具。除了使底部导航等常见模式的设置更容易之外,该组件还处理后台堆栈,fragment 切换,参数传递,基于导航的动画和深层链接。重要的是,它会收集所有这些导航信息,并将其放在你应用程序的一个可视化的导航图。并支持 Deeplink 。

二.Navigation 的配置

1.在project的build.gradle 添加如下:

2.在app的build.gradle 添加如下:

三.Navigation 的简单跳转

1.添加导航图(类似iOS开发中的StoryBoard):

  • 击res目录,选择New > Android resource file
  • New Resource对话中输入文件名nav_graph_main.xml,选择Resource type为Navigation

点击OK后IDE会在navigation目录下生成nav_graph_main.xml文件

2.在Activity布局中指定Navigation的宿主(Host):

其中,fragment的name一定要是androidx.navigation.fragment.NavHostFragment,app:navGraph输入刚刚生成的导航图位置。

覆写onSupportNavigateUp()方法,如果app:defaultNavHost="true" 表示使用默认的导航host,自动覆盖Activity的back按钮,不用再覆写[AppCompatActivity.onSupportNavigateUp()]

3.自己创建多个fragmet.xml 如下

内容类似如下

4.Fragment.class 代码实现如下

5.Fragment 当中的跳转传参

传参通过Buddle,详见如下:

接收参数如下:

6.返回上前一个Fragment 方法

四、默认的参数跳转设置:

描述:可以用来保证参数不为空

在project的build.gradle当中添加

在app的build.gradle 当中添加

在nav_graph_main.xml 当中添加

argument 有三个属性 name、defaultValue 和 type,

  • name 就是名字到时会生成这个名字的 set 和 get 方法,
  • defaultValue 是默认值,
  • type 就是数据类型,有以下几种可以使用

五、设置跳转的动画

在nav_graph_main.xm当中配置

六、导航文件 nav_graph_main.xm 说明

id: 就像写布局的 id 那样需要给个 id 才能找到它

name: 指定哪个 Fragment 类名

tools:layout: fragment的layout

id :就是这个 action 的 id。

destination:是目的地,要跳转到哪里的。

还可以设置动画

点击下面的Design查看下:

七、Deeplink 的支持

在AndroidMainifest.xml 里面设置:

在 Navigation 里面设置:

八、类似 ViewPage 的支持

九、源码分析

我花了一些时间绘制了 Navigation的UML类图,我坚信,这种方式能帮助你我 更深刻的理解 Navigation的整体架构:

设计 NavHostFragment

NavHostFragment 应当有两个作用:

  • 作为Activity导航界面的载体
  • 管理并控制导航的行为

前者的作用我们已经说过了,我们通过在NavHostFragment的创建时,为它创建一个对应的FrameLayout作为 导航界面的载体


Group container, @Nullable Bundle savedInstanceState) {
	FrameLayout frameLayout = new FrameLayout(inflater.getContext());  
	frameLayout.setId(getId());??
	return?frameLayout;  
 }

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable View

我们都知道代码设计应该遵循 单一职责原则,因此,我们应该将 管理并控制导航的行为 交给另外一个类,这个类的作用应该仅是 控制导航行为,因此我们命名为 NavController

Fragment理应持有这个NavController的实例,并将导航行为 委托 给它,这里我们将 NavController 的持有者抽象为一个 接口,以便于以后的拓展。

于是我们创造了 NavHost 接口,并让NavHostFragment实现了这个接口:

public?interface?NavHost?{??
  NavController?getNavController();
}

为了保证导航的 安全,NavHostFragment 在其 作用域 内,理应 有且仅有一个NavController 的实例

这里我们驻足一下,请注意API的设计,似乎 Navigation.findNavController(View),参数中传递任意一个 view的引用似乎都可以获取 NavController——如何保证 NavController 的局部单例呢?

事实上,findNavController(View)内部实现是通过 遍历 View树,直到找到最底部 NavHostFragment 中的NavController对象,并将其返回的:

private static NavController findViewNavController(@NonNull View view) {

        while (view != null) {

           NavController controller = getViewNavController(view);

            if (controller != null) {

                return controller;

            }

            ViewParent parent = view.getParent();

            view = parent instanceof View ? (View) parent : null; 
        }
        return null;
  }    

设计 NavController

站在 设计者 的角度,NavController 的职责是:

  • 1.对navigation资源文件夹下nav_graph.xml的 解析
  • 2.通过解析xml,获取所有 Destination(目标点)的 引用 或者 Class的引用
  • 3.记录当前栈中 Fragment的顺序
  • 3.管理控制 导航行为

NavController 持有了一个 NavInflater ,并通过 NavInflater 解析xml文件。

这之后,获取了所有 Destination(在本文中即Page1Fragment , Page2Fragment , Page3Fragment ) 的 Class对象,并通过反射的方式,实例化对应的 Destination,通过一个队列保存:

private NavInflater mInflater;  //NavInflater

private NavGraph mGraph;        //解析xml,得到NavGraph

private int mGraphId;           //xml对应的id,比如 nav_graph_main

//所有Destination的队列,用来处理回退栈
 private final Deque<NavDestination> mBackStack = new ArrayDeque<>();

这看起来没有任何问题,但是站在 设计者 的角度上,还略有不足,那就是,Navigation并非只为Fragment服务

先不去吐槽Google工程师的野心,因为现在我们就是他,从拓展性的角度考虑,Navigation是一个导航框架,今后可能 并非只为Fragment导航

我们应该为要将导航的 Destination 抽象出来,这个类叫做 NavDestination ——无论 Fragment 也好,Activity 也罢,只要实现了这个接口,对于NavController 来讲,他们都是 Destination(目标点)而已。

对于不同的 NavDestination 来讲,它们之间的导航方式是不同的,这完全有可能(比如Activity 和 Fragment),如何根据不同的 NavDestination 进行不同的 导航处理 呢?

NavDestination 和 Navigator

有同学说,我可以这样设计,通过 instanceof 关键字,对 NavDestination 的类型进行判断,并分别做出处理,比如这样:

if (destination instanceof Fragment) {

  //对应Fragment的导航

} else if (destination instanceof Activity) {

  //对应Activity的导航

}

这是OK的,但是不够优雅,Google的方式是通过抽象出一个类,这个类叫做 Navigator

public abstract class Navigator<D extends NavDestination> {

   //省略很多代码,包括部分抽象方法,这里仅阐述设计的思路! 
   //导航 
   public abstract void navigate(@NonNull D destination, @Nullable Bundle args, 

                                    @Nullable NavOptions navOptions);

   //实例化NavDestination(就是Fragment) 
  public abstract D createDestination();

    //后退导航 
   public abstract boolean popBackStack();
}

Navigator(导航者) 的职责很单纯:

  • 1.能够实例化对应的 NavDestination
  • 2.能够指定导航
  • 3.能够后退导航

你看,我的 NavController 获取了所有 NavDestination 的Class对象,但是我不负责它 如何实例化 ,也不负责 如何导航 ,也不负责

如何后退 ——我仅仅持有向上的引用,然后调用它的接口方法,它的实现我不关心。

FragmentNavigator为例,我们来看看它是如何执行的职责:

public?class?FragmentNavigator?extends?Navigator<FragmentNavigator.Destination>?{?????//省略大量非关键代码,请以实际代码为主!???@Override???public?boolean?popBackStack()?{????????return?mFragmentManager.popBackStackImmediate();?   }?  @NonNull?  @Override  public?Destination?createDestination()?{????//?实际执行了好几层,但核心代码如下,通过反射实例化Fragment???????Class<??extends?Fragment>?clazz?=?getFragmentClass();??????return??clazz.newInstance();  }???@Override??public?void?navigate(@NonNull?Destination?destination,?@Nullable?Bundle?args,?@Nullable?NavOptions?navOptions)???????//?实际上还是通过FragmentTransaction进行的跳转处理??????final?Fragment?frag?=?destination.createFragment(args);??????final?FragmentTransaction?ft?=?mFragmentManager.beginTransaction();??????ft.replace(mContainerId,?frag);??????ft.commit();??????mFragmentManager.executePendingTransactions();???}}


不同的 Navigator 对应不同的 NavDestinationFragmentNavigator 对应的是 FragmentNavigator.Destination,你可以把他理解为案例中的 Fragment ,有兴趣的朋友可以自己研究一下。

总结:

优点:减少Fragment 当中跳转相关的代码 ,更符合单一职责设计原理。缺点:多了一个xml。

Demo 地址 :

Navigation 跳转Demo:

https://github.com/YuriyPiKachu/NavigationDemo.git

Navigation 底部导航栏切换Demo :

https://github.com/YuriyPiKachu/NavigationAdvancedSample.git

Navigation 官网:

https://developer.android.google.cn/guide/navigation/

相关推荐

土豪农村建个别墅不新鲜 建个车库都用框架结构?

农村建房子过去都是没车库,也没有那么多豪车,一般直接停在路边或者院子里。现在很多人都会在建房子的时候留一个车库,通过车库可以直接进入客厅,省得雨雪天气折腾。农村土豪都是有钱任性,建房子跟我们普通人不一...

自建框架结构出现裂缝怎么回事?

三层自建房梁底与墙体连接处裂缝是结构问题吗?去前帮我姑画了一份三层自建房的图纸,前天他们全部装修好了。我姑丈突然打电话给我说他发现二层的梁底与墙分离了,有裂缝。也就是图纸中前面8.3米那跨梁与墙体衔接...

钢结构三维图集-框架结构(钢柱对接)

1、实腹式钢柱对接说明1:1.上节钢柱的安装吊点设置在钢柱的上部,利用四个吊点进行吊装;2.吊装前,下节钢柱顶面和本节钢柱底面的渣土和浮锈要清除干净,保证上下节钢柱对接面接触顶紧;3.钢柱吊装到位后...

三层框架结构主体自建房设计案例!布局13*12米占地面积156平米!

绘创意设计乡村好房子设计小编今日头条带来分享一款:三层框架结构主体自建房设计案例!布局13*12米占地面积156平米!本案例设计亮点:这是一款三层新中式框架结构自建房,占地13×12米,户型占地面积...

Casemaker机箱框架结构3D图纸 STEP格式

农村自建房新宠!半框架结构凭啥这么火?内行人揭开3个扎心真相

回老家闲逛,竟发现个有意思的现象:村里盖新房,十家有八家都选了"半框架结构"。隔壁王叔家那栋刚封顶的二层小楼,外墙红砖还露着糙面没勾缝,里头的水泥柱子倒先支棱得笔直,这到底是啥讲究?蹲...

砖混结构与框架结构!究竟有何区别?千万别被坑!

农村自建房选结构,砖混省钱但出事真能保命吗?7月建材价格波动期,多地建房户因安全焦虑陷入选择困境——框架结构虽贵30%,却是地震区保命的关键。框架柱和梁组成的承重体系,受力分散得像一张网。砖混靠墙硬扛...

砖混结构与框架结构,究竟有何区别?千万别被坑!

农村建房选砖混结构还是框架结构?这个问题算是近期留言板里问得最多的问题了。今天咱们说说二者的区别,帮您选个合适的。01成本区别假如盖一栋砖混结构的房子需要30万,那么换成框架结构,一般要多掏30%的费...

6个小众却逆天的App神器,个个都是黑科技的代表

你的手机上有哪些好用的软件?今天我就给大家分享6个小众却逆天的App神器,个个都是黑科技的代表!01*Via浏览器推荐理由:体积极小的浏览器,没有任何广告。使用感受:它的体量真的很小,只有702KB,...

合肥App开发做一个app需要多少钱?制作周期有多久?

在移动互联网时代,开发一款APP已成为企业数字化转型与个人创业的重要途径。然而,APP的开发成本与制作周期受功能复杂度、技术架构、团队类型等多重因素影响,差异极大。好牛软件将从这两个维度展开分析,帮助...

详解应对App臃肿化的五大法则

编者注:本文转自腾讯ISUX。先来看一张图:图上看到,所有平台上用户花费时间都在减少,除了移动端。观察身边也是如此,回家不开电脑的小伙伴越来越多。手机平板加电视,下班场景全搞定。连那些以前电脑苦手的...

实战!如何从零搭建10万级 QPS 大流量、高并发优惠券系统

需求背景春节活动中,多个业务方都有发放优惠券的需求,且对发券的QPS量级有明确的需求。所有的优惠券发放、核销、查询都需要一个新系统来承载。因此,我们需要设计、开发一个能够支持十万级QPS的券系...

8种移动APP导航设计模式大对比

当我们确定了移动APP的设计需求和APP产品设计流程之后,开始着手设计APP界面UI或是APP原型图啦。这个时候我们都要面临的第一个问题就是如何将信息以最优的方式组合起来?也许我们对比和了解了其他一些...

数字资产支付 App 的技术框架

开发一款功能强大、安全可靠的数字资产支付App需要一个整合了区块链技术、后端服务、前端应用以及第三方集成的全栈技术框架。这个框架的核心在于保障数字资产的安全流通,并将其高效地桥接到传统的法币支付场...

从MyBatis到App架构:设计模式全景应用指南

从MyBatis到App架构:设计模式全景应用指南引言在企业级应用和服务端开发领域,MyBatis凭借其灵活、简洁、强大的ORM映射能力被广泛应用。而它之所以能拥有如此优秀的可扩展性和工程可维护性,正...

取消回复欢迎 发表评论: