- Navigation Architecture Componen
- Navigation Architecture Componen
- Navigation Architecture Componen
- Navigation Architecture Componen
- Navigation Architecture Componen
- 初尝Android Jetpack 之Navigation
- 浅谈 Android Architecture Componen
- 关于 Android Architecture Componen
- 基于 Android Architecture Componen
- Architecture -- Navigation
Navigation Architecture Component 的一些概念
导航原则
任何应用内导航的目标应该是为用户提供一致且可预测的体验,为了实现这一目标,导航架构组件可帮助您构建符合以下几个导航原则的应用程序。
- 堆栈用于表示应用程序的“导航状态”
- 前进(Up)按钮永远不会退出您的应用
- 前进(Up)和后退(Back)在您的应用程序任务中是等效的
- 如果一直后退不能退出你的应用,那就跟前进没什么两样了。
- 深度链接到目标或导航到同一目标应产生相同的堆栈
- 应该是跟Activity任务栈管理一致的意思吧
目标 destination
一个destination是你在你的App上能导航到的任何地方,虽然destination通常是碎片(Fragment),但是Navigation Architecture Component也支持其它类型的destination:
- Activity
- 导航图(navigation graph)或者次级导航图(navigation subgraph)
- 自定义的destination类型
操作 action
一个导航图与目标之间的联系称作操作,下图就展示了一个应用的导航图,来自于一个包含了6个目标,目标之间被5个action连接的应用

提示:如果要在Android Studio中使用导航架构组件,则必须使用Android Studio 3.2 Canary 14或更高版本。不知道AS 3.2正式版好久出,应该快了。
角色
NaviHostFragmnet
简单例子
1. 实现类似首页多Fragment的切换
效果

这个业务很常见,一般用于应用首页,但如图切换的四个Fragment并不是前进与后退关系,而是同级横向切换关系。
以前的实现方案
我自己考虑的实现方案是优先使用Android官方库
- 使用FragmentTabHost
设置好Tab和对应的Fragment,FragmentTabHost可以自动帮我们做tab和Fragment之间的绑定,切换Fragment的业务代码也给我们封装好了。不过FragmentTabHost的切换方案是先add所有的fragment,再执行dettach上一个fragment,attach要切换的fragment。所以每次切换到fragment,fragment的生命周期都会从onCreateView开始执行,一直执行到onResume,这一点也要考虑下。
- 使用BottomNavigationView + 自己写的切换代码
FragmentTabHost虽然帮我们实现了切换,但是切换Fragment的方式也是固定的,如果我想fragment之前已经被add显示过,之后再切换到它,不执行它的onCreateView直接show行不行,当然行,那就要自己的这份代码了。
- 使用TabLayout + 自己写的切换代码
这个跟方案二差不多,不过通常都搭配ViewPager使用
- 自定义Tab + 自己写的切换代码
这个较2,3方案就是导航的View也是自己定义,一般用于简单的导航需求。
试试BottomNavigationView+Navigation 新方案
我比较关注Navigation是怎样实现Fragment切换的
1.创建NavHostFragment
NavHostFragment可以看作放置显示Fragment的位置容器控件,本质就是个Fragment。
跟Fragment的用法差不多,有两种创建方式
- 静态方式,贴在xml布局文件上
比如我这里,activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:layout_width="match_parent"
android:id="@+id/main_nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/nav_graph"
app:defaultNavHost="true"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<android.support.design.widget.BottomNavigationView
android:layout_width="match_parent"
android:id="@+id/main_bottom_nav"
app:menu="@menu/main_bottom_nav"
android:background="@drawable/main_nav_background"
android:layout_height="60dp"/>
</LinearLayout>
Activity创建的时候会自动解析xml,把fragment添加进Fragmentmanger,在Activity中,通过 (NavHostFragment) fragmentManager.findFragmentById(R.id.main_nav_host)就能获取到你添加的NavHostFragment实例。
- java代码动态创建
官方例子:
NavHostFragment finalHost = NavHostFragment.create(R.navigation.example_graph);
getSupportFragmentManager().beginTransaction()
.replace(R.id.nav_host, finalHost)
.setPrimaryNavigationFragment(finalHost) // this is the equivalent to app:defaultNavHost="true"
.commit();
NavHostFragment有create方法获取一个新实例,然后添加到fragmentManager里,跟xml配置差不多,还是需要一个容器,setPrimaryNavigationFragment(finalHost)是设置back键拦截
一些参数设置
BottomNavigationView的菜单参数,main_bottom_nav.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/oneFragment"
android:icon="@drawable/ic_one"
android:title="@string/title_one" />
<item
android:id="@+id/twoFragment"
android:icon="@drawable/ic_two"
android:title="@string/title_two" />
<item
android:id="@+id/threeFragment"
android:icon="@drawable/ic_three"
android:title="@string/title_three" />
<item
android:id="@+id/fourFragment"
android:icon="@drawable/ic_four"
android:title="@string/title_four" />
</menu>
四个Fragment

代码略
naviHostFragment的参数
NaviHostFragment的navGraph参数,像菜单一样也可用xml写,这是很方便的,用xml就意味着AS有GUI界面能够让你拖拽点点点生成代码。
所以现在项目的res又有了新类型的资源文件:navigation
如果需要AS支持navigation资源文件编辑,还要再Settings里面开启Enable Navigation Editor

比如我现在四个碎片的例子,nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@id/oneFragment">
<fragment
android:name="me.newtrekwang.navigationdemo.nav1.OneFragment"
android:id="@+id/oneFragment"
android:label="one_fragment"
tools:layout="@layout/one_fragment"
/>
<fragment
android:name="me.newtrekwang.navigationdemo.nav2.TwoFragment"
android:id="@+id/twoFragment"
android:label="two_fragment"
tools:layout="@layout/two_fragment"
/>
<fragment
android:id="@+id/threeFragment"
android:name="me.newtrekwang.navigationdemo.nav3.ThreeFragment"
android:label="three_fragment"
tools:layout="@layout/three_fragment" />
<fragment
android:id="@+id/fourFragment"
android:name="me.newtrekwang.navigationdemo.nav4.FourFragment"
android:label="four_fragment"
tools:layout="@layout/four_fragment" />
</navigation>
我把navigation里包含的每个标签看作是目标,所以目标就必须有自己的id等属性,比如fragment,id为目标id,name为目标完成类名,label是标签,layout方便AS解析预览。
nav_graph预览

可以看到预览里,总动解析出host为MainActivity那个navHostFragment,以及四个我新定义的Fragment,oneFragment标记为start,表示为起始显示的Fragment.
因为现在是四个碎片之间横向切换,所以他们之间没有Action
BottomNavigationView与NavController绑定
回到MainActivity
public class MainActivity extends AppCompatActivity {
private BottomNavigationView bottomNavigationView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bottomNavigationView = findViewById(R.id.main_bottom_nav);
BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);
NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager().findFragmentById(R.id.main_nav_host);
NavController navController = navHostFragment.getNavController();
NavigationUI.setupWithNavController(bottomNavigationView,navController);
}
@Override
public boolean onSupportNavigateUp() {
return Navigation.findNavController(this,R.id.main_nav_host).navigateUp();
}
}
Navigation Architecture Component 的设计是导航都通过NaviController负责导航,NavController可以通过三种静态方法获得
- NavHostFragment.findNavController(Fragment)
- Navigation.findNavController(Activity, @IdRes int viewId)
- Navigation.findNavController(View)
NavHostFragment.findNavController(fragment)就是不断遍历fragment的getParentFragment(),直到找到一个NavHostFragment实例,然后返回它的navController.我们这里MainActivty很显然可以直接获取到navHostFragment,然后调用getNavController即刻。
Navigation.findNavController(Activity, @IdRes int viewId),Navigation.findNavController(View)其实是一个方法,最终会遍历view的 view.getParent(),直到找到tag为NavController的View,然后返回controller。
所以获取NavController的来源一种是NavHostFragment,另一种是tag为NaviController的View。
最后NavigationUI.setupWithNavController(bottomNavigationView,navController),NavigationUI把bottomNavigationView和navController绑定在一起,这样一个碎片切换的业务几行代码就搞定了,大家可以实践下,貌似比之前的方案少了很多代码量。
NavigationUI 怎么替我们绑定bottomNavigationView和navController的?
NavigationUI替我们实现的切换是啥样的?Fragment切换时的生命周期如何?令我有点小失望的是,每次切换到新Fragment都会new 一次Fragment,然后执行它的生命周期方法,是的,就是重新new 一次,再replace,你可以再Fragment里加几个log验证一下。
网友评论