Navigation分析

705 阅读3分钟
  • name 必须指定为以下值,这是切换fragment的容器android:name="androidx.navigation.fragment.NavHostFragment"
  • defaultNavHost 表示是否拦截返回键,默认为false。
  • 如果使用xml实现,fragment务必设置id。navGraph 用来表示上面的导航意图文件 navigation_jetpack.xml
  <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        android:background="?android:attr/windowBackground"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/bottom_nav_menu" />

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toTopOf="@id/nav_view"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />

navigation.xml

navigation根节点 startDestination 表示第一个显示的fragment HomeFragment

fragment 节点中name属性表示所属的fragment类;

fragment 节点中action节点destination属性用于指定下一个目标fragment

fragment 节点中argument 用于传递数据。表示的是传递到当前Fragment的数据,Key为name属性,默认数据是android:defaultValue,数据类型是argType。 deepLink:通过uri拉起当前页面

<navigation 
    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:id="@+id/mobile_navigation"    
    app:startDestination="@+id/navigation_home">    
    <fragment        
        android:id="@+id/navigation_home"        
        android:name="com.example.mmdemo.ui.home.HomeFragment"        
        android:label="@string/title_home"        
        tools:layout="@layout/fragment_home">        
        <action            
            android:id="@+id/action_home"            
            app:destination="@id/navigation_dashboard" />        
        <argument            
            android:name="arg1"            
            android:defaultValue="home_arg"            
            app:argType="string" />        
        <deepLink            
            android:id="@+id/deepLink"            
            app:uri="www.baidu.com" />    
    </fragment>
</navigation>

NavHostFragment

NavHost:显示 Navigation graph 中目标的空白容器。Navigation 组件包含一个默认 NavHost 实现 (NavHostFragment),可显示 Fragment 目标。NavHostFragment在布局中提供了一个区域,用于进行包含导航。

 public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final Context context = requireContext();

        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(mNavController);

(1)初始化NavController,NavController为导航的控制类,核心类。 (2)onCreateNavController(mNavController);

/*添加导航器*/
  @CallSuper
    protected void onCreateNavController(@NonNull NavController navController) {
        navController.getNavigatorProvider().addNavigator(
                new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
        navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
    }
    
    /*构造函数添加导航器*/
 public NavController(@NonNull Context context) {
        mContext = context;
        while (context instanceof ContextWrapper) {
            if (context instanceof Activity) {
                mActivity = (Activity) context;
                break;
            }
            context = ((ContextWrapper) context).getBaseContext();
        }
        mNavigatorProvider.addNavigator(new NavGraphNavigator(mNavigatorProvider));
        mNavigatorProvider.addNavigator(new ActivityNavigator(mContext));
    }

通过构造和这里逻辑后,mNavigatorProvider容器内有四条记录 mNavigatorProvider.addNavigator() 添加navigator HashMap<String, Navigator<? extends NavDestination>> mNavigators = new HashMap<>();

1"navigation",NavGraphNavigator)
2"activity",ActivityNavigator)
3"dialog",DialogFragmentNavigator)
4 ("fragment","FragmentNavigator")

2,3,4给具体页面提供具体导航能力的 1、

分析几种Navigator

父类 public abstract class Navigator<D extends NavDestination> NavDestination:代表一个个的页面 这样的设计是一种类型的Navigator只能创建一种类型的页面的节点信息

该注释应被添加到每个子类导航来表示用于登记与导航器的默认名称

    @Retention(RUNTIME)
    @Target({TYPE})
    @SuppressWarnings("UnknownNullness") // TODO https://issuetracker.google.com/issues/112185120
    public @interface Name {
        String value();
    }

可以通过name获取NavDestination的实例

    /**
     * NavDestinations should be created via {@link Navigator#createDestination}.
     */
    public NavDestination(@NonNull String navigatorName) {
        mNavigatorName = navigatorName;
    }

了解一个Navigator需要了解两个方法

  • createDestination()
  • navigate()

一、ActivityNavigator

1、createDestination
public Destination createDestination() { 
    return new Destination(this);}

public Destination(@NonNull Navigator<? extends Destination> activityNavigator) {       super(activityNavigator);}

//This constructor requires that the given Navigator has a 
{@link Navigator.Name} annotation.

public NavDestination(@NonNull Navigator<? extends NavDestination> navigator) {    
    this(NavigatorProvider.getNameForNavigator(navigator.getClass()));}


创建一个页面节点
传递了一个activityNavigator对象,
获得Navigator.Name复制给mNavigatorName
2、navigate

同理 DialogFragmentNavigator FragmentNavigator

四、NavGraphNavigator

1、createDestination

public NavGraph createDestination() {    return new NavGraph(this);}

在NavGraph创建节点信息

SparseArrayCompat<NavDestination> mNodes = new SparseArrayCompat<>();

在NavHostFragment>onCreate中创建NavGraph

mNavController.setGraph(mGraphId);

    @CallSuper
    public void setGraph(@NavigationRes int graphResId, @Nullable Bundle startDestinationArgs) {
        setGraph(getNavInflater().inflate(graphResId), startDestinationArgs);
    }
解析资源文件
   @SuppressLint("ResourceType")
    @NonNull
    public NavGraph inflate(@NavigationRes int graphResId) {
        Resources res = mContext.getResources();
        XmlResourceParser parser = res.getXml(graphResId);
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        、、、
        NavDestination destination = inflate(res, parser, attrs, graphResId);
            if (!(destination instanceof NavGraph)) {
                throw new IllegalArgumentException("Root element <" + rootElement + ">"
                        + " did not inflate into a NavGraph");
            }
            return (NavGraph) destination;        
NavInflater.inflate
  • parser.getName()是mobile_navigation中节点fragment,activity或者dialog;Argument,action标签
    @NonNull
    private NavDestination inflate(@NonNull Resources res, @NonNull XmlResourceParser parser,
            @NonNull AttributeSet attrs, int graphResId)
            throws XmlPullParserException, IOException {
        Navigator<?> navigator = mNavigatorProvider.getNavigator(parser.getName());
        final NavDestination dest = navigator.createDestination();

        dest.onInflate(mContext, attrs);
        、、、
        while(){
            @NonNull
    private NavDestination inflate(@NonNull Resources res, @NonNull XmlResourceParser parser,
            @NonNull AttributeSet attrs, int graphResId)
            throws XmlPullParserException, IOException {
        Navigator<?> navigator = mNavigatorProvider.getNavigator(parser.getName());
        final NavDestination dest = navigator.createDestination();

        dest.onInflate(mContext, attrs);
        }
        return dest;
}
2、navigate

做完解析,把工作委托给其他三个navigator

    @Nullable
    @Override
    public NavDestination navigate(@NonNull NavGraph destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Extras navigatorExtras) {
        int startId = destination.getStartDestination();
        if (startId == 0) {
            throw new IllegalStateException("no start destination defined via"
                    + " app:startDestination for "
                    + destination.getDisplayName());
        }
        NavDestination startDestination = destination.findNode(startId, false);
        if (startDestination == null) {
            final String dest = destination.getStartDestDisplayName();
            throw new IllegalArgumentException("navigation destination " + dest
                    + " is not a direct child of this NavGraph");
        }
        Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
                startDestination.getNavigatorName());
        return navigator.navigate(startDestination, startDestination.addInDefaultArgs(args),
                navOptions, navigatorExtras);
    }

navigation各个类置间的关系

929.png