谈navigation使用姿势

原文地址:https://blog.csdn.net/weixin_42009003/article/details/80702990

fragment是官方给出处理页面功能块分离重用的方案,它有完整的生命周期,几乎可以做到activity可以做到的一切,并且同样的界面Activity占用内存比Fragment要多,总之用fragment好处多多,为啥也有一些前辈大神都建议在项目中尽量避免使用fragment,比如 Square:从今天开始抛弃Fragment吧!不得不说它也有很多坑的地方,如果你只是使用viewpager+fragment方式可以忽略。如果想要实现单activity多fragment的方式,就需要自己管理fragment栈,github上也有很多大神呕心沥血写出很多针对管理fragment的经验,能帮助我们解决很多问题,可以参考fragmentation ,但是这里不是讲fragmentation框架,大家有兴趣可以去看他的源码,还是非常赞的。

这里要讲的是谷歌推出navigation的框架,这里只是讲它的使用,对源码感兴趣的同学可以看android-navigation

导包目前是1.0.0的测试版

在project的build.gradle中添加

classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0-alpha02'

在app的build.gradle中添加

apply plugin: 'androidx.navigation.safeargs'
implementation 'android.arch.navigation:navigation-fragment:1.0.0-alpha02'
implementation 'android.arch.navigation:navigation-ui:1.0.0-alpha02'

然后在res资源中增加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/launcher_home">
    android:id="@+id/launcher_home"
    android:name="com.example.android.codelabs.navigation.MainFragment"
    android:label="@string/home"
    tools:layout="@layout/main_fragment">
     android:name="step"
    app:type="integer"
    android:defaultValue="1" />

    android:id="@+id/next_action"
    app:destination="@+id/flow_step_one"
    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:popUpTo="@id/launcher_home"/>

navigation 标签中有一个属性app:startDestination="@+id/launcher_home"   指定默认启动的fragmentid

fragment标签中id和name就不多说了,在fragment的内部标签

argument 一看便知是带参

还有一个action标签,里面有两个属性destination 定义目标fragment的Id,popUpTo是回退的fragment id

fragment开发和之前一样,在宿主activity里需要重写onSupportNavigateUp方法去启动fragment,如下

@Override
public boolean onSupportNavigateUp() {
    return Navigation.findNavController(getActivity(), R.id.host_fragment).navigateUp();
}

fragment之间跳转调用

Navigation.findNavController(v).navigate(R.id.action_test2);
它会根据当前fragment在navigation.xml里的定义的fragment节点,解析获取它的fragment信息以及action和argument节点,拿到所有跳转需要所有的数据。

Navigation.findNavController(v).navigateUp();

是fragment栈内回退方法,如果fragment栈内没有则执行activity的回退方法。

这样就可以开始开发单activity多fragment的app了,是不是有点小激动?

转入开发,你会发现navigation.xml里面配置的东西是不是有点多?如果action不配置转场动画,它转换是可以,但是体验只能呵呵了,还有destination 目标fragment,不配置它怎么跳转?这样fragment多起来岂不要炸了?带着疑问看源码!

Navigation.findNavController(v)返回一个控制器,它的navigate方法入参有两种,一个是

public void navigate(@IdRes int resId, @Nullable Bundle args, @Nullable NavOptions navOptions)

另一个可以传入一个NavDirections,这是个什么东东???经过一番查找,发现了它的所在,它是我们配置navigation.xml里每个fragment的action时,工具为我们生成了一些类。文件名以fragment名后追加Directions的类

public class MainFragmentDirections {
  public static Action_test1 action_test1() {
    return new Action_test1();
  }

  public static class Action_test1 implements NavDirections {
    public Action_test1() {
    }

    public Bundle getArguments() {
      Bundle __outBundle = new Bundle();
      return __outBundle;
    }

    public int getActionId() {
      return com.winky.douniwan.R.id.action_test1;
    }
  }
}

xml配置一大堆就算了,还生成一堆类,顿时感觉到深深的恶意,还要用吗?有没有方法不用配置那个action?而且你看

public void navigate(@NonNull NavDirections directions, @Nullable NavOptions navOptions) {
    navigate(directions.getActionId(), directions.getArguments(), navOptions);
}

方法最终调用和传id进去是一样的,这样只能再仔细看看这个方法,先贴源码,

public void navigate(@IdRes int resId, @Nullable Bundle args, @Nullable NavOptions navOptions) {
    NavDestination currentNode = mBackStack.isEmpty() ? mGraph : mBackStack.peekLast();
    if (currentNode == null) {
        throw new IllegalStateException("no current navigation node");
    }
    @IdRes int destId = resId;
    final NavAction navAction = currentNode.getAction(resId);
    if (navAction != null) {
        if (navOptions == null) {
            navOptions = navAction.getNavOptions();
        }
        destId = navAction.getDestinationId();
    }
    if (destId == 0 && navOptions != null && navOptions.getPopUpTo() != 0) {
        popBackStack(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive());
        return;
    }

    if (destId == 0) {
        throw new IllegalArgumentException("Destination id == 0 can only be used"
                + " in conjunction with navOptions.popUpTo != 0");
    }

    NavDestination node = findDestination(destId);
    if (node == null) {
        final String dest = NavDestination.getDisplayName(mContext, destId);
        throw new IllegalArgumentException("navigation destination " + dest
                + (navAction != null
                ? " referenced from action " + NavDestination.getDisplayName(mContext, resId)
                : "")
                + " is unknown to this NavController");
    }
    if (navOptions != null) {
        if (navOptions.shouldClearTask()) {
            // Start with a clean slate
            popBackStack(mGraph.getId(), true);
        } else if (navOptions.getPopUpTo() != 0) {
            popBackStack(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive());
        }
    }
    node.navigate(args, navOptions);
}

之前传入的actionId 就是第一个参数,bundle显然是附带参数,第三个参数里面存放的是进场退场动画属性,它先会根据这个resid去找action节点解析,如果不为空则将配置action里面的属性全部取出来,会根据action里面的destination去查找目标fragment,如果NavAction 为空!它就使用入参的resid去找fragment!!!谷歌工程师想的还是非常周到的,这样就可以不必在navigation.xml里去为每个fragment配置action和argument了。

然后去掉fragment配置的action,xml里面简洁多了,基本和manifest里差不多,只增加一个fragment标签,为它配置类路径及id即可。

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/fragment_main">

            android:id="@+id/fragment_main"
        android:name="com.winky.douniwan.ui.fragment.MainFragment" />

            android:id="@+id/fragment_test1"
        android:name="com.winky.douniwan.ui.fragment.FragmentTest1" />
            android:id="@+id/fragment_test2"
        android:name="com.winky.douniwan.ui.fragment.FragmentTest2" />

            android:id="@+id/fragment_test3"
        android:name="com.winky.douniwan.ui.fragment.FragmentTest3" />
跳转时

NavOptions options = new NavOptions.Builder()
        .setEnterAnim(R.anim.slide_right_in)
        .setExitAnim(R.anim.slide_left_out)
        .setPopEnterAnim(R.anim.slide_left_in)
        .setPopExitAnim(R.anim.slide_right_out)
        .build();

Navigation.findNavController(v).navigate(R.id.fragment_test1, null, options);
传入fragment的id,附带参数,以及转场动画。NavOptions 可以缓存全局使用,是不是很棒!如果本文对你使用navigation有帮助,请评论赞个呗!

你可能感兴趣的:(移动架构)