美文网首页
[学习]JetPack 中的 Navigation(二)

[学习]JetPack 中的 Navigation(二)

作者: 吴敬悦 | 来源:发表于2021-01-29 22:55 被阅读0次

    页面之间的参数传递。我现在主要学习官方提供的安全的方式。

    看官网: 使用 Safe Args 传递安全的数据

    使用安全的方式首先需要做两件准备工作:

    1. 在项目下的 build.gradle 中的:
    buildscript {
        repositories {
            google()
        }
        dependencies {
            def nav_version = "2.3.2"
            classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
        }
    }
    
    1. app 下的 build.gradle 中添加(这里仅说 java ):
    apply plugin: "androidx.navigation.safeargs"
    

    这样 ide 就会自动帮你生成一个类,比如你的 FragmentHomeFragment ,那么就生成一个叫 HomeFragmentDirections 的类,这个类下面的方法或属性也不是都是固定的,会根据你 action 中的 id 自动生成,这里的 action

    navigation 图示
    图中的箭头,对应的 id 就是图中右边的 Attributes 下的 id 那一栏。自动帮我们生成的类下面有一个属性(内部类)和一个方法,方法的名称就是上面 id 的小驼峰写法,类的名称就是大驼峰法。比如我的是 toDetail ,那么对应的方法名称是 toDetail ,类名称是 ToDetail

    我们知道页面跳转传递参数,那么就需要定义参数,参数的定义也是在 res/navigation/ 下的你的 xml 文件中,如果 Design 的方式,见下图,我们看到右边 Attributes 下面有一个 Arguments 属性,这里就可以很方便的设置所需要传递的属性;这里是设置当前页面的属性,也就是从其他页面跳转过来需要传递的属性,并不是跳转前的属性。我这里是从 HomeFragment 页面跳转到 DetailFragment 页面,所以我需要设置的属性是在 DetailFragment 页面。

    Arguments设置

    如果设置好了属性,那么在生成这个类的时候也会生成对应的 gettersetter 。比如我这里在 DetailFragment 页面添加了 name 属性,在下面生成的代码中可以看到对应的方法。

    如果当前页面可以跳转到多个页面,每个页面都有对应的属性,这些属性都会在对应的内部类中。比如我这里的 name 属性,它是在 ToDetail 内部类中的。

    自动生成的类为(这里我的,参考):

    public class HomeFragmentDirections {
      private HomeFragmentDirections() {
      }
    
      @NonNull
      public static ToDetail toDetail() {
        return new ToDetail();
      }
    
      public static class ToDetail implements NavDirections {
        private final HashMap arguments = new HashMap();
    
        private ToDetail() {
        }
    
        @NonNull
        public ToDetail setName(@NonNull String name) {
          if (name == null) {
            throw new IllegalArgumentException("Argument \"name\" is marked as non-null but was passed a null value.");
          }
          this.arguments.put("name", name);
          return this;
        }
    
        @Override
        @SuppressWarnings("unchecked")
        @NonNull
        public Bundle getArguments() {
          Bundle __result = new Bundle();
          if (arguments.containsKey("name")) {
            String name = (String) arguments.get("name");
            __result.putString("name", name);
          } else {
            __result.putString("name", "吴天歌");
          }
          return __result;
        }
    
        @Override
        public int getActionId() {
          return R.id.toDetail;
        }
    
        @SuppressWarnings("unchecked")
        @NonNull
        public String getName() {
          return (String) arguments.get("name");
        }
    
        @Override
        public boolean equals(Object object) {
          if (this == object) {
              return true;
          }
          if (object == null || getClass() != object.getClass()) {
              return false;
          }
          ToDetail that = (ToDetail) object;
          if (arguments.containsKey("name") != that.arguments.containsKey("name")) {
            return false;
          }
          if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
            return false;
          }
          if (getActionId() != that.getActionId()) {
            return false;
          }
          return true;
        }
    
        @Override
        public int hashCode() {
          int result = 1;
          result = 31 * result + (getName() != null ? getName().hashCode() : 0);
          result = 31 * result + getActionId();
          return result;
        }
    
        @Override
        public String toString() {
          return "ToDetail(actionId=" + getActionId() + "){"
              + "name=" + getName()
              + "}";
        }
      }
    }
    

    理解上面的,接下来就可以跳转并传入参数了,在我的 HomeFragment 页面中添加如下函数:

    @Override
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
      super.onViewStateRestored(savedInstanceState);
      Log.e(LOG, "onViewStateRestored");
      Button button = requireView().findViewById(R.id.homeButton);
      button.setOnClickListener(v -> {
        NavController controller = Navigation.findNavController(v);
        HomeFragmentDirections.ToDetail action = HomeFragmentDirections.toDetail();
        action.setName("吴敬悦");
        controller.navigate(action);
      });
    }
    

    说完了跳转出发页面,下面再说跳转目的地页面。

    同样的也会生成对应的类,这里的规则是目的地页面的类名后面添加 Args ,得到了新的类。比如我的是 DetailFragment 页面,自动生成的类名为 DetailFragmentArgs ,这个类会自动根据设置的属性生成 getter 。下面是我这边自动生成的类:

    public class DetailFragmentArgs implements NavArgs {
      private final HashMap arguments = new HashMap();
    
      private DetailFragmentArgs() {
      }
    
      private DetailFragmentArgs(HashMap argumentsMap) {
        this.arguments.putAll(argumentsMap);
      }
    
      @NonNull
      @SuppressWarnings("unchecked")
      public static DetailFragmentArgs fromBundle(@NonNull Bundle bundle) {
        DetailFragmentArgs __result = new DetailFragmentArgs();
        bundle.setClassLoader(DetailFragmentArgs.class.getClassLoader());
        if (bundle.containsKey("name")) {
          String name;
          name = bundle.getString("name");
          if (name == null) {
            throw new IllegalArgumentException("Argument \"name\" is marked as non-null but was passed a null value.");
          }
          __result.arguments.put("name", name);
        } else {
          __result.arguments.put("name", "吴天歌");
        }
        return __result;
      }
    
      @SuppressWarnings("unchecked")
      @NonNull
      public String getName() {
        return (String) arguments.get("name");
      }
    
      @SuppressWarnings("unchecked")
      @NonNull
      public Bundle toBundle() {
        Bundle __result = new Bundle();
        if (arguments.containsKey("name")) {
          String name = (String) arguments.get("name");
          __result.putString("name", name);
        } else {
          __result.putString("name", "吴天歌");
        }
        return __result;
      }
    
      @Override
      public boolean equals(Object object) {
        if (this == object) {
            return true;
        }
        if (object == null || getClass() != object.getClass()) {
            return false;
        }
        DetailFragmentArgs that = (DetailFragmentArgs) object;
        if (arguments.containsKey("name") != that.arguments.containsKey("name")) {
          return false;
        }
        if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
          return false;
        }
        return true;
      }
    
      @Override
      public int hashCode() {
        int result = 1;
        result = 31 * result + (getName() != null ? getName().hashCode() : 0);
        return result;
      }
    
      @Override
      public String toString() {
        return "DetailFragmentArgs{"
            + "name=" + getName()
            + "}";
      }
    
      public static class Builder {
        private final HashMap arguments = new HashMap();
    
        public Builder(DetailFragmentArgs original) {
          this.arguments.putAll(original.arguments);
        }
    
        public Builder() {
        }
    
        @NonNull
        public DetailFragmentArgs build() {
          DetailFragmentArgs result = new DetailFragmentArgs(arguments);
          return result;
        }
    
        @NonNull
        public Builder setName(@NonNull String name) {
          if (name == null) {
            throw new IllegalArgumentException("Argument \"name\" is marked as non-null but was passed a null value.");
          }
          this.arguments.put("name", name);
          return this;
        }
    
        @SuppressWarnings("unchecked")
        @NonNull
        public String getName() {
          return (String) arguments.get("name");
        }
      }
    }
    

    我们知道以前页面传参数是根据 Bundle 进行传参的,同样的从其他页面传过来的参数都会保存到 Bundle 所对应对象中,每一个 Fragment 提供有一个方法可以获取到当前页面得到的参数:

    // Fragment.java
    /**
     * Return the arguments supplied when the fragment was instantiated,
     * if any.
     */
    @Nullable
    final public Bundle getArguments() {
        return mArguments;
    }
    

    为了安全起见,一般不会直接调用这个方法,而是 requireArguments

    // Fragment.java
    /**
     * Return the arguments supplied when the fragment was instantiated.
     *
     * @throws IllegalStateException if no arguments were supplied to the Fragment.
     * @see #getArguments()
     */
    @NonNull
    public final Bundle requireArguments() {
        Bundle arguments = getArguments();
        if (arguments == null) {
            throw new IllegalStateException("Fragment " + this + " does not have any arguments.");
        }
        return arguments;
    }
    

    再结合上面自动生成的类,代码可以为:

    // DetailFragment
    @Override
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
      super.onViewStateRestored(savedInstanceState);
      DetailFragmentArgs args = DetailFragmentArgs.fromBundle(requireArguments());
      TextView textView  = requireView().findViewById(R.id.textView);
      textView.setText(args.getName());
      Log.e(LOG, "onViewStateRestored");
    }
    

    相关文章

      网友评论

          本文标题:[学习]JetPack 中的 Navigation(二)

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