Android工程模板Bottom Navigation Activity分析
- 首先观察生成工程的AndroidManifest文件,其中只有一个Activity。
- 运行程序,可以看到底部是三个导航栏,上面是内容区域,点击不同的导航栏tab就显示不同的内容。
- 分析入口Activity,查看布局文件。
- 底部导航栏通过BottomNavigationView实现,具体的导航栏按钮通过app:menu来指定menu资源。
- 内容区域是通过静态使用fragment来实现的,通过android:name指定具体的Fragment是androidx.navigation.fragment.NavHostFragment;除此以外,还有app:defaultNavHost和app:navGraph属性比较重要。
app:defaultNavHost是用来表示Fragment是否可以拦截系统返回键;app:navGraph指定navigation资源,这个资源能够指定页面间的路由结构(也就是跳转方式)。在这个navigation资源文件中,其根标签是navigation,根标签中的app:startDestination属性指定默认显示的fragment页面;子标签就是一个个不同的静态fragment,这些fragment中可以添加argument、action、deepLink子标签,其中argument子标签表示跳转到当前fragment所需要的参数;action表示当前页跳转至目标页的动作,它是通过app:destination属性指定跳转的fragment的id来连接不同的destination的;deepLink通过指定app:uri的方式拉起当前页面。
NavHostFragment分析
以下代码的分析会涉及到NavGraphNavigator、ActivityNavigator、DialogFragmentNavigator、FragmentNavigator,这四个类都继承Navigator。Navigator是一个抽象类,它定义了在应用中导航的机制。除此以外,Navigator需要一个继承自NavDestination的泛型,这个NavDestination代表整个导航图中的一个页面节点,具体的页面就是Activity、DialogFragment还是Fragment。在Navigator中有一个Name注解,它的说明是This annotation should be added to each Navigator subclass to denote the default name used to register the Navigator with a {@link NavigatorProvider}.简单来说就是Navigator子类必须在类上标有Name注解,这个Name是用来注册到NavigatorProvider中的。Navigator中有一个方法
@NonNull public abstract D createDestination();
,当创建页面节点时,会解析Navigator子类上的Name注解,NavDestination的构造器需要具体的Navigator对象或者这个Navigator类的Name注解,这个Navigator的Name能够取得对应Navigator实例以完成页面跳转;具体的跳转是实现navigate方法;是否拦截系统返回键是实现popBackStack方法;onSaveState和onRestoreState的实现是用来进行状态保存和恢复的操作的;Extras接口是用来处理特定行为的,比如转场动画。
@CallSuper
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Context context = requireContext();
// 这里创建一个NavHostController对象,NavHostController类是NavController的扩展类,
// 通过NavHost来连接NavController和其它依赖。
// NavHostController的构造器通过super调用NavController的构造器。
// NavController类是通过NavHost组织app内导航的类。
// 在NavController构造器中,有这样两行代码:
// mNavigatorProvider.addNavigator(new NavGraphNavigator(mNavigatorProvider));
// mNavigatorProvider.addNavigator(new ActivityNavigator(mContext));
// mNavigatorProvider是NavigatorProvider类的对象,这个类用于存储
// 一系列的合法跳转到目的地的Navigators。
// 在NavigatorProvider类中有这样一个属性:
// private final HashMap> mNavigators = new HashMap<>();
// 这个mNavigators是一个HashMap,用来存储所有的Navigators,也就是导航器实例,
// 其中的key是Navigator子类标记的Name注解,value就是对应Navigator实例。
// ActivityNavigator是Activity的导航器。
// NavGraphNavigator是当navigation资源解析完成后跳转到startDestination的导航器。
mNavController = new NavHostController(context);
mNavController.setLifecycleOwner(this);
mNavController.setOnBackPressedDispatcher(requireActivity().getOnBackPressedDispatcher());
// Set the default state - this will be updated whenever
// onPrimaryNavigationFragmentChanged() is called
mNavController.enableOnBackPressed(
mIsPrimaryBeforeOnCreate != null && mIsPrimaryBeforeOnCreate);
mIsPrimaryBeforeOnCreate = null;
mNavController.setViewModelStore(getViewModelStore());
// onCreateNavController方法调用了下面两个语句
// navController.getNavigatorProvider()
// .addNavigator(new DialogFragmentNavigator(requireContext(),getChildFragmentManager()));
// navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
// 上面两个语句分别是添加DialogFragment和Fragment的导航器。
onCreateNavController(mNavController);
Bundle navState = null;
if (savedInstanceState != null) {
navState = savedInstanceState.getBundle(KEY_NAV_CONTROLLER_STATE);
if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) {
mDefaultNavHost = true;
getParentFragmentManager().beginTransaction()
.setPrimaryNavigationFragment(this)
.commit();
}
mGraphId = savedInstanceState.getInt(KEY_GRAPH_ID);
}
if (navState != null) {
// Navigation controller state overrides arguments
mNavController.restoreState(navState);
}
if (mGraphId != 0) {
// Set from onInflate()
mNavController.setGraph(mGraphId);
} else {
// See if it was set by NavHostFragment.create()
final Bundle args = getArguments();
final int graphId = args != null ? args.getInt(KEY_GRAPH_ID) : 0;
final Bundle startDestinationArgs = args != null
? args.getBundle(KEY_START_DESTINATION_ARGS)
: null;
if (graphId != 0) {
mNavController.setGraph(graphId, startDestinationArgs);
}
}
}
ActivityNavigator分析
- 在ActivityNavigator类上标注了@Navigator.Name("activity")的注解,说明了当ActivityNavigator添加到NavigatorProvider中的mNavigators的HashMap成员变量时,key是"activity",而value是ActivityNavigator本身的实例。
- createDestination():创建Activity页面节点
// 以下是ActivityNavigator类的方法
// ......
@NonNull
@Override
public Destination createDestination() {
return new Destination(this);
}
// ......
// 以下是ActivityNavigator的静态内部类Destination的方法
// ......
public Destination(@NonNull Navigator extends Destination> activityNavigator) {
super(activityNavigator);
}
// ......
// 以下是NavDestination类
// ......
public NavDestination(@NonNull Navigator extends NavDestination> navigator) {
this(NavigatorProvider.getNameForNavigator(navigator.getClass()));
}
public NavDestination(@NonNull String navigatorName) {
mNavigatorName = navigatorName;
}
// ......
首先是在ActivityNavigator中创建它的静态内部类Destination的对象,在这个静态内部类Destination构造器中调用它父类即NavDestination的构造器,然后获取对应的Navigator的Name。这个静态内部类Destination中提供了一些创建Intent对象的方法。
- navigate方法:通过第2步中创建的Intent对象来进行跳转。
DialogFragmentNavigator分析
- 它的注解变成了@Navigator.Name("dialog"),其余与ActivityNavigator类似。
- createDestination():创建DialogFragment页面节点。
与ActivityNavigator中第2步类似,也是通过静态内部类的方式将Name传递给NavDestination。
在这个静态内部类Destination中有setClassName和getClassName方法来设置和获取与此Destination关联的DialogFragment类名。
- navigate方法:获取第2步中的DialogFragment全类名,判断它是否是DialogFragment,最后通过DialogFragment对象的show展示DialogFragment。
FragmentNavigator分析
- 它的注解变成了@Navigator.Name("fragment"),其余与DialogFragmentNavigator类似。
- createDestination():创建Fragment页面节点,与DialogFragmentNavigator中第2步类似。
- navigate方法与DialogFragmentNavigator类似,只不过最后是通过FragmentTransaction对象的replace展示fragment。
NavGraphNavigator分析
- 它的注解变成了@Navigator.Name("navigation"),其余与FragmentNavigator类似。
- createDestination():创建了一个NavGraph对象
// 这是NavGraphNavigator类
// ......
@NonNull
@Override
public NavGraph createDestination() {
return new NavGraph(this);
}
// ......
// 这是NavGraph类
// 这个mNodes属性是用来存储一个个的页面节点的
@SuppressWarnings("WeakerAccess") /* synthetic access */
final SparseArrayCompat mNodes = new SparseArrayCompat<>();
// ......
public NavGraph(@NonNull Navigator extends NavGraph> navGraphNavigator) {
super(navGraphNavigator);
}
// ......
首先是在NavGraphNavigator中创建NavGraph对象,在NavGraph类的构造器中调用它父类即NavDestination的构造器,然后获取对应的Navigator的Name。NavGraph是按ID获取的{@link NavDestination}节点的集合,它是一个虚拟的destination,本身并不会加入回退栈而是将start destination加入到回退栈。实际上配置navigation资源文件的作用就是为了生成NavGraph对象,最后将其和NavController关联。
- navigate方法实际上是先获取默认页面的start destination,然后获取navigator实例,具体的navigate就是根据navigator是fragment、activity还是dialogfragment执行特定的navigate。
Navigation分析总结
- NavHostFragment:作为内容区域的承载者
- NavHostController:NavController的扩展
- NavController:组织App内的导航。
- Navigator:定义导航机制,内置子类包括NavGraphNavigator、ActivityNavigator、FragmentNavigator、DialogFragmentNavigator。
- NavGraph:所有{@link NavDestination}节点的集合,它是一个虚拟的destination,将start destination加入到了回退栈。
- NavDestination:代表了在整个NavGraph中的一个页面节点,子类包括ActivityNavigator.Destination、FragmentNavigator.Destination、DialogFragmentNavigator.Destination、NavGraph。
- NavigatorProvider:使用mNavigators的HashMap成员变量存储所有Navigator。