美文网首页
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