android final参数,Android应用开发之Android Jetpack-Navigation 使用中参数的传递方法

本文将带你了解Android应用开发之Android Jetpack-Navigation 使用中参数的传递方法,希望本文对大家学Android有所帮助。

由于使用了Navigation,导致Fragment的创建行为完全交给了系统。也就是说,以前的那种通过#Fragment.newInstance(Args...)方式传递参数的方式就被切断了,没有办法快乐的在fragment之间传参了~~。但是不要担心,google爸爸早就帮我们想好了方式,下面我们来一条一条的看看吧:

1.通过NavController.navigate(int, bundle)方法传参。

这个是Navigation中提供的最原始的一种参数传递的方法,我们只需要在使用这个函数导航的时候,将原先需要在newInstance()中传递的bundle对象放在改方法的第二个参数中。系统就会帮我们将bundle传递到目标fragment/activity的argument/Intent中,只需要和之前一样,在跳转到的fragment/activity中调用getArgument()/getIntent(),就可以获得我们传递的Bundle对象。我们来看看源码,探索下系统到底怎么是帮我们实现的吧!(没有兴趣的同学可以直接跳过)

public final void navigate(@IdRes   int resId, @Nullable Bundle args)   {    navigate(resId, args, null);}

可以看到在这个方法体中系统只是简单的帮我们做了转调。我们继续:

NavController.java:

public void navigate(@IdRes   int resId, @Nullable Bundle args,   @Nullable NavOptions navOptions)   {...    node.navigate(args, navOptions);}

NavDestination.java:

public void navigate(@Nullable   Bundle args, @Nullable NavOptions   navOptions) {    Bundle defaultArgs =   getDefaultArguments();    Bundle finalArgs = new Bundle();    finalArgs.putAll(defaultArgs);    if   (args != null)   {        finalArgs.putAll(args);    }    mNavigator.navigate(this,   finalArgs, navOptions);}

public abstract void   navigate(@NonNull D destination,   @Nullable Bundle   args,                                 @Nullable   NavOptions navOptions);

最后我们可以看到,它指向了到了一个抽象类(Navigator)中的抽象方法。

这个抽象方法具体有3个主要实现:

1.ActivityNavigator.navigate(...):

@Overridepublic   void navigate(@NonNull Destination destination,   @Nullable Bundle   args,        @Nullable   NavOptions navOptions) {    if   (destination.getIntent() == null)   {        throw new   IllegalStateException("Destination " +   destination.getId()                +   " does not have an Intent   set.");    }    Intent   intent = new Intent(destination.getIntent());    if (args   != null)   {        intent.putExtras(args);        String   dataPattern = destination.getDataPattern();        if   (!TextUtils.isEmpty(dataPattern))   {            //   Fill in the data pattern with the args to build a valid   URI            StringBuffer   data = new   StringBuffer();            Pattern   fillInPattern =   Pattern.compile("\\{(.+)\\}");            Matcher   matcher =   fillInPattern.matcher(dataPattern);            while   (matcher.find()) {                String   argName =   matcher.group(1);                if   (args.containsKey(argName)) {                    matcher.appendReplacement(data,   "");                    data.append(Uri.encode(args.getString(argName)));                }   else {                    throw   new IllegalArgumentException("Could not find "   + argName + " in   "                              + args + " to fill data pattern "   + dataPattern);                }            }            matcher.appendTail(data);            intent.setData(Uri.parse(data.toString()));        }    }    if   (navOptions != null && navOptions.shouldClearTask())   {        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);    }    if   (navOptions != null && navOptions.shouldLaunchDocument()            &&   Build.VERSION.SDK_INT >=   Build.VERSION_CODES.LOLLIPOP)   {        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);    }   else if (!(mContext instanceof Activity))   {        // If we're not launching   from an Activity context we have to launch in a new   task.        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);    }    if   (navOptions != null && navOptions.shouldLaunchSingleTop())   {        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);    }    if   (mHostActivity != null) {        final   Intent hostIntent =   mHostActivity.getIntent();        if   (hostIntent != null)   {            final   int hostCurrentId = hostIntent.getIntExtra(EXTRA_NAV_CURRENT,   0);            if   (hostCurrentId != 0)   {                intent.putExtra(EXTRA_NAV_SOURCE,   hostCurrentId);            }        }    }    final   int destId = destination.getId();    intent.putExtra(EXTRA_NAV_CURRENT,   destId);    NavOptions.addPopAnimationsToIntent(intent,   navOptions);    mContext.startActivity(intent);    if   (navOptions != null && mHostActivity != null)   {        int enterAnim =   navOptions.getEnterAnim();        int   exitAnim = navOptions.getExitAnim();        if   (enterAnim != -1 || exitAnim != -1)   {            enterAnim   = enterAnim != -1  enterAnim :   0;            exitAnim   = exitAnim != -1  exitAnim :   0;            mHostActivity.overridePendingTransition(enterAnim,   exitAnim);        }    }     //   You can't pop the back stack from the caller of a new   Activity,    // so we don't add this navigator to the   controller's back stack    dispatchOnNavigatorNavigated(destId,   BACK_STACK_UNCHANGED);}

2.FragmentNavigator.navigate(...)

@Overridepublic   void navigate(@NonNull Destination destination,   @Nullable Bundle   args,                        @Nullable   NavOptions navOptions) {    final Fragment   frag = destination.createFragment(args);    final   FragmentTransaction ft =   mFragmentManager.beginTransaction();     int   enterAnim = navOptions != null  navOptions.getEnterAnim() :   -1;    int exitAnim = navOptions != null    navOptions.getExitAnim() : -1;    int popEnterAnim =   navOptions != null  navOptions.getPopEnterAnim() :   -1;    int popExitAnim = navOptions != null    navOptions.getPopExitAnim() : -1;    if (enterAnim != -1   || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1)   {        enterAnim = enterAnim !=   -1  enterAnim :   0;        exitAnim = exitAnim !=   -1  exitAnim : 0;        popEnterAnim   = popEnterAnim != -1  popEnterAnim :   0;        popExitAnim = popExitAnim   != -1  popExitAnim :   0;        ft.setCustomAnimations(enterAnim,   exitAnim, popEnterAnim,   popExitAnim);    }     ft.replace(mContainerId,   frag);     final StateFragment oldState =   getState();    if (oldState != null)   {        ft.remove(oldState);    }     final   @IdRes int destId =   destination.getId();    final StateFragment newState =   new StateFragment();    newState.mCurrentDestId =   destId;    ft.add(newState,   StateFragment.FRAGMENT_TAG);     final   boolean initialNavigation = mFragmentManager.getFragments().isEmpty();    final   boolean isClearTask = navOptions != null &&   navOptions.shouldClearTask();    // TODO Build first   class singleTop behavior for fragments    final boolean isSingleTopReplacement   = navOptions != null && oldState !=   null            &&   navOptions.shouldLaunchSingleTop()            &&   oldState.mCurrentDestId == destId;    if   (!initialNavigation && !isClearTask &&   !isSingleTopReplacement)   {        ft.addToBackStack(getBackStackName(destId));    }   else {        ft.runOnCommit(new   Runnable()   {            @Override              public void run()   {                dispatchOnNavigatorNavigated(destId,   isSingleTopReplacement                         BACK_STACK_UNCHANGED                          :   BACK_STACK_DESTINATION_ADDED);            }        });    }    ft.commit();    mFragmentManager.executePendingTransactions();}

3.NavGraphNavigation.navigate(...):

@Overridepublic   void navigate(@NonNull NavGraph destination,   @Nullable Bundle   args,        @Nullable   NavOptions navOptions) {    int startId =   destination.getStartDestination();    if (startId == 0)   {        throw new   IllegalStateException("no start destination defined   via"                  + " app:startDestination for "                  + (destination.getId() !=   0                         NavDestination.getDisplayName(mContext,   destination.getId())                        :   "the root   navigation"));    }    NavDestination   startDestination = destination.findNode(startId,   false);    if (startDestination == null) {        final   String dest = NavDestination.getDisplayName(mContext,   startId);        throw new   IllegalArgumentException("navigation destination "   +   dest                +   " is not a direct child of this   NavGraph");    }    dispatchOnNavigatorNavigated(destination.getId(),   BACK_STACK_DESTINATION_ADDED);    startDestination.navigate(args,   navOptions);}

看了源码是不是觉得其实很简单。

主要注意的是第三个NavGraphNavigator它其实也是一个转调,他将这个Bundle传递给了startDestination的navigate方法。也就是回到了NavDestination的navigate方法,其实也就是分情况调用ActivityNavigator或FragmentNavigator的Navigate。

2.通过ViewModle传参(只支持Fragment导航)

ViewModel也是Android JetPack中引入的一个lib(其实之前在android架构组建里已经被引入了),主要是为了解决在android/Fragment中onConfigrationChange导致的状态丢失问题,和Fragment的参数传递问题,因为很多时候依附在同一个activity伤的多个fragment其实是如果一个逻辑单元的。这个我将来应该也会专门介绍,不了解的同学可以把它想象成Activity/fragment的一个内部属性或者可以看看网上其他的关于ViewModel的教程。

在Fragment中使用:

?1ViewModelProviders.of(FragmentActivity)

获得Activity的ViewModel,只要多Fragment attach到的是同一个activity对象。那么他们返回ViewModel就是同一个,这样就不用关心Fragment。而只用在ViewModel中保存需要的属性,他们就可以在不同fragment之间共享啦。

3.通过官方专门添加的lib SafeArgs

需要在你的Root Project中添加如下依赖,添加后你的root Project依赖部分应该大致如下(注意红色部分):

buildscript   {    apply   from:'dependencies.gradle'      repositories {        google()        jcenter()    }     dependencies   {        classpath   'com.android.tools.build:gradle:3.2.0-alpha14'          classpath   "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"          classpath   "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0-alpha01"          // NOTE: Do not place your application dependencies here; they   belong        // in the inpidual   module build.gradle files    }}

并在你需要使用safeArgs的project的build.gradle中引入这个插件,引入后你的build.gradle大致如下(还是注意红色部分):

apply   plugin:   'com.android.application'apply   plugin: 'kotlin-android'apply   plugin:   'kotlin-android-extensions'apply   plugin:   'kotlin-kapt'apply   plugin: 'androidx.navigation.safeargs'android   {    compileSdkVersion   sdk_version    defaultConfig   {        applicationId   "com.example.xwh.myapplication"          minSdkVersion   rootProject.ext.mini_sdk_version        versionCode   1        versionName   "1.0"          testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"      }    buildTypes   {        release   {            minifyEnabled   false            proguardFiles   getDefaultProguardFile('proguard-android.txt'),   'proguard-rules.pro'          }    }    dexOptions   {        // Sets the maximum number   of DEX processes        // that can   be started   concurrently.        maxProcessCount   8        // Sets the maximum memory   allocation pool size        // for   the dex   operation.        javaMaxHeapSize   "2g"          // Enables Gradle to pre-dex library dependencies.        preDexLibraries   true    }} dependencies   {    //do not modify dependencies here, modify in root   project's depencencies.gradle    implementation   fileTree(include:   ['*.jar'], dir:   'libs')    def deps =   rootProject.ext.project_dependencies.get(getProject().getName())    deps.libs.each   { dep -> implementation dep }    deps.textLibs.each {   dep -> testImplementation dep   }    deps.androidTestLibs.each { dep ->   androidTestImplementation dep   }    //java    //deps.annceationProcessors.each   { dep -> annotationProcessor dep   }    //kotlin    deps.annceationProcessors.each   { dep -> kapt dep }    deps.projects.each { dep ->   implementation project dep }    //add other ext   dependencies below }

然后你就可以在你的nav_graph.xml中通过 右侧的click+to add arguments按钮添加safeArgs了

添加之后要怎么做呢?你需要build这个项目,这个时候android studio会自动帮你生成几个类。他的生成规则是:你原Fragment的名称+'Directions',比如你的framgnet名称是FragmentA 那他就会帮你生成一个叫做FragmentADirections的类,这个类里还有一个静态内部类,名字为你在xml中定义的Action,如果你你这个fragment标签里有argument子标签(即配置过safeArg)。那么,这个静态内部类还会为你生成这些参数作为他的属性。这样你就可以愉快的传递参数了。传递参数的关键代码如下:

nav_graph.xml:

"@+id/fragmentStep2"      tools:layout="@layout/fragment_step2"      android:name="com.example.xwh.myapplication.FragmentStep2"      android:label="FragmentStep2"   >    "test"          android:defaultValue="11111"          app:type="integer"   />     "@+id/action_fragmentStep2_to_fragmentStep3"          app:destination="@id/fragmentStep3"          app:enterAnim="@anim/nav_default_enter_anim"          app:exitAnim="@anim/nav_default_exit_anim"          app:popEnterAnim="@anim/nav_default_pop_enter_anim"          app:popExitAnim="@anim/nav_default_pop_exit_anim"   />

跳转到Fragment的部分代码:(kotlin)

val directions =   FragmentStep1Directions.action_step1_to_step2().run   {    setTest(111)}Navigation.findNavController(it)        .navigate(directions)

自动生成的类:

public class FragmentStep1Directions   {  public static Action_step1_to_step2 action_step1_to_step2()   {    return new   Action_step1_to_step2();  }   public static class   Action_step1_to_step2 implements NavDirections   {    private int test =   11111;     public Action_step1_to_step2()   {    }     public   Action_step1_to_step2 setTest(int test) {      this.test   = test;      return   this;    }     public Bundle   getArguments() {      Bundle __outBundle = new   Bundle();      __outBundle.putInt("test",   this.test);      return   __outBundle;    }     public int   getActionId() {      return   com.example.xwh.myapplication.R.id.action_step1_to_step2;    }  }}

参数接收:

val test =   FragmentStep2Args.fromBundle(arguments).test

可以看到safeArgs还会帮你生成一个叫做"你的fragment名称"+‘Args’名称的类,其实参数真正的传递者还是fragment的arguments属性。

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!

你可能感兴趣的:(android,final参数)