美文网首页
Navigation源码简单分析

Navigation源码简单分析

作者: 有点健忘 | 来源:发表于2018-06-15 17:43 被阅读54次

    navigation的使用,网上资料很多,就不弄了,有空再补充。

    随时补充

    如下代码

    <?xml version="1.0" encoding="utf-8"?>
    <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"
        app:startDestination="@id/fragmentHome">
        <action
            android:id="@+id/go_StepOne"
            app:destination="@id/fragmentStepOne" />
        <fragment
            android:id="@+id/fragmentHome"
            android:name="com.charliesong.demo0327.navigation.FragmentHome"
            android:label="FragmentHome"
            tools:layout="@layout/nav_fragment_home">
    
        </fragment>
        <fragment
            android:id="@+id/fragmentStepOne"
            android:name="com.charliesong.demo0327.navigation.FragmentStepOne"
            android:label="FragmentStepOne"
            tools:layout="@layout/nav_fragment_step_one">
            <argument
                android:name="title"
                android:defaultValue="just test" />
            <argument
                android:name="title2"
                android:defaultValue="just test2" />
            <action
                android:id="@+id/fragmentStepOne1"
                app:enterAnim="@anim/slide_in_right"
                app:exitAnim="@anim/slide_out_left"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right"
                app:destination="@id/fragmentStepTwo" />
        </fragment>
    
        <fragment
            android:id="@+id/fragmentStepOne1"
            android:name="com.charliesong.demo0327.navigation.FragmentStepOne"
            android:label="FragmentStepOne"
            tools:layout="@layout/nav_fragment_step_one">
            <action
                android:id="@+id/go_StepTwo"
                app:enterAnim="@anim/slide_in_right"
                app:exitAnim="@anim/slide_out_left"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right"
                app:destination="@id/fragmentStepTwo" />
        </fragment>
    
        <fragment
            android:id="@+id/fragmentStepTwo"
            android:name="com.charliesong.demo0327.navigation.FragmentStepTwo"
            android:label="FragmentStepTwo"
            tools:layout="@layout/nav_fragment_step_two">
            <action
                android:id="@+id/go_StepOne"
                app:destination="@id/fragmentStepOne" />
            <action
                android:id="@+id/go_home"
                app:popUpTo="@id/fragmentHome"/>
        </fragment>
    
    
    </navigation>
    

    说明:action标签可以写在fragment外边,也可以是里边,区别在于。
    写在外边,这个id哪里都可以用。写在fragment里边的,只有这个fragment可以用,其他fragment用这个action的id就会报错。
    问题:
    因为我基类里写了如下的代码,也就是使用了toolbar的后退键

        override fun onOptionsItemSelected(item: MenuItem): Boolean {
            when(item.itemId){
                android.R.id.home->{
                    onBackPressed()
    //忘了加 return true了,所以引发了下边的执行了2次后退操作
                }
            }
            return super.onOptionsItemSelected(item)
        }
    

    而现在加了navigation以后,我们的fragment也要拦截后退键的

    override fun onSupportNavigateUp(): Boolean = findNavController(my_nav_host_fragment).navigateUp()
    

    然后就发现,点击手机上的物理后退键是没有任何问题了。fragment是正常的一次后退一个。
    可点击toolbar上的后退箭头,就发现一次退了至少2个fragment。然后去看下了代码
    如下AppCompatActivity里,果然也处理 android.R.id.home,这等于处理了2次。

        public final boolean onMenuItemSelected(int featureId, android.view.MenuItem item) {
            if (super.onMenuItemSelected(featureId, item)) {
          //super的代码在下边,由于忘了写return true了,所以后退执行了2次。
                return true;
            }
    
            final ActionBar ab = getSupportActionBar();
            if (item.getItemId() == android.R.id.home && ab != null &&
                    (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
                return onSupportNavigateUp();
            }
            return false;
        }
    
    

    再看下Activity里的代码

        public boolean onMenuItemSelected(int featureId, MenuItem item) {
            CharSequence titleCondensed = item.getTitleCondensed();
    
            switch (featureId) {
                case Window.FEATURE_OPTIONS_PANEL:
                    // Put event logging here so it gets called even if subclass
                    // doesn't call through to superclass's implmeentation of each
                    // of these methods below
                    if(titleCondensed != null) {
                        EventLog.writeEvent(50000, 0, titleCondensed.toString());
                    }
                    if (onOptionsItemSelected(item)) {
                        return true;
                    }
    

    首先Navigation这个工具类

    公开的静态方法就是下边这几个


    image.png

    然后我们看下获取
    可以看到,是对我们传如的view,读取getTag来得到的,如果没有,就找view的parent。
    需要注意的是,如果没找到,它就直接抛出异常了。所以传的view要确保有


    image.png

    然后看下设置,就是把controller设置为tag

        public static void setViewNavController(@NonNull View view,
                @Nullable NavController controller) {
            view.setTag(R.id.nav_controller_view_tag, controller);
        }
    

    看看哪里设置的,就是系统的NavHostFragment


    image.png

    我们在布局里一般这么写的,所以如上图所注释的,我们的controller就是设置给了这个fragment的View拉。

       <fragment
           android:layout_width="match_parent"
           android:layout_height="0dp"
           android:layout_weight="1"
           android:id="@+id/my_nav_host_fragment"
           android:name="androidx.navigation.fragment.NavHostFragment"
           app:navGraph="@navigation/mobile_navigation"
           app:defaultNavHost="true"
           />
    

    另外获取controller也可以通过如下方法
    NavHostFragment里有

    public static NavController findNavController(@NonNull Fragment fragment)
    

    或者

        public NavController getNavController() {
            if (mNavController == null) {
                throw new IllegalStateException("NavController is not available before onCreate()");
            }
            return mNavController;
        }
    

    需要注意的是controller是在NavHostFragment的onCreate方法里创建的,所以在这个生命周期之前获取不到的。

      @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            final Context context = getContext();
    
            mNavController = new NavController(context);
            mNavController.getNavigatorProvider().addNavigator(createFragmentNavigator());
    

    相关文章

      网友评论

          本文标题:Navigation源码简单分析

          本文链接:https://www.haomeiwen.com/subject/njqasftx.html