AndroidJetpack-Navigation使⽤中参数的传递
由于使⽤了Navigation,导致Fragment的创建⾏为完全交给了系统。也就是说,以前的那种通过#)⽅式传递参数的⽅式就被切断了,没有办法快乐的在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(...):
@Override
public void navigate(@NonNull Destination destination, @Nullable Bundle args,
@Nullable NavOptions navOptions) {
if (Intent() == null) {
throw new IllegalStateException("Destination " + Id()
+ " does not have an Intent set.");
}
Intent intent = new Intent());
if (args != null) {
intent.putExtras(args);
安卓intent用法String dataPattern = DataPattern();
if (!TextUtils.isEmpty(dataPattern)) {
if (!TextUtils.isEmpty(dataPattern)) {
/
/ Fill in the data pattern with the args to build a valid URI
StringBuffer data = new StringBuffer();
Pattern fillInPattern = Patternpile("\\{(.+?)\\}");
Matcher matcher = fillInPattern.matcher(dataPattern);
while (matcher.find()) {
String argName = up(1);
if (ainsKey(argName)) {
matcher.appendReplacement(data, "");
data.String(argName)));
} else {
throw new IllegalArgumentException("Could not find " + argName + " in "
+ args + " to fill data pattern " + dataPattern);
}
}
matcher.appendTail(data);
intent.setData(Uri.String()));
}
}
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 = Intent();
if (hostIntent != null) {
final int hostCurrentId = IntExtra(EXTRA_NAV_CURRENT, 0);
if (hostCurrentId != 0) {
intent.putExtra(EXTRA_NAV_SOURCE, hostCurrentId);
}
}
}
final int destId = Id();
intent.putExtra(EXTRA_NAV_CURRENT, destId);
NavOptions.addPopAnimationsToIntent(intent, navOptions);
mContext.startActivity(intent);
if (navOptions != null && mHostActivity != null) {
int enterAnim = EnterAnim();
int exitAnim = ExitAnim();
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(...)
@Nullable NavOptions navOptions) {
final Fragment frag = ateFragment(args);
final FragmentTransaction ft = mFragmentManager.beginTransaction();
int enterAnim = navOptions != null ? EnterAnim() : -1;
int exitAnim = navOptions != null ? ExitAnim() : -1;
int popEnterAnim = navOptions != null ? PopEnterAnim() : -1;
int popExitAnim = navOptions != null ? PopExitAnim() : -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);
}
final StateFragment oldState = getState();
if (oldState != null) {
}
final @IdRes int destId = Id();
final StateFragment newState = new StateFragment();
newState.mCurrentDestId = destId;
ft.add(newState, StateFragment.FRAGMENT_TAG);
final boolean initialNavigation = Fragments().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);
}
});
}
ftmit();
}
3.NavGraphNavigation.navigate(...):
@Nullable NavOptions navOptions) {
int startId = StartDestination();
if (startId == 0) {
throw new IllegalStateException("no start destination defined via"
+ " app:startDestination for "
+ (Id() != 0
? DisplayName(mContext, Id())
: "the root navigation"));
}
NavDestination startDestination = destination.findNode(startId, false);
if (startDestination == null) {
final String dest = DisplayName(mContext, startId);
throw new IllegalArgumentException("navigation destination " + dest
+ " is not a direct child of this NavGraph");
}
Id(), BACK_STACK_DESTINATION_ADDED);
startDestination.navigate(args, navOptions);
}
看了源码是不是觉得其实很简单。
主要注意的是第三个NavGraphNavigator它其实也是⼀个转调,他将这个Bundle传递给了startDestinat
ion的navigate⽅法。也就是回到了NavDestination的navigate⽅法,其实也就是分情况调⽤ActivityNavigator或FragmentNavigator的Navigate。
2.通过ViewModle传参(只⽀持Fragment导航)
ViewModel也是Android JetPack中引⼊的⼀个lib(其实之前在android架构组建⾥已经被引⼊了),主要是为了解决在
android/Fragment中onConfigrationChange导致的状态丢失问题,和Fragment的参数传递问题,因为很多时候依附在同⼀个activity 伤的多个fragment其实是如果⼀个逻辑单元的。这个我将来应该也会专门介绍,不了解的同学可以把它想象成Activity/fragment的⼀个内部属性或者可以看看⽹上其他的关于ViewModel的教程。
在Fragment中使⽤:
ViewModelProviders.of(FragmentActivity)
获得Activity的ViewModel,只要多Fragment attach到的是同⼀个activity对象。那么他们返回ViewModel就是同⼀个,这样就不⽤关⼼Fragment。⽽只⽤在ViewModel中保存需要的属性,他们就可以在不同fragment之间共享啦。
3.通过官⽅专门添加的lib SafeArgs
需要在你的Root Project中添加如下依赖,添加后你的root Project依赖部分应该⼤致如下(注意红⾊部分):
buildscript {
apply from:'adle'
repositories {
google()
jcenter()
}
dependencies {
classpath 'ls.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 individual adle files
}
}
并在你需要使⽤safeArgs的project的adle中引⼊这个插件,引⼊后你的adle⼤致如下(还是注意红⾊部分):
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 "application"
ini_sdk_version
versionCode 1
versionName "1.0"
testInstrumentationRunner "st.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile(''), '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'adle
implementation fileTree(include: ['*.jar'], dir: 'libs')
def deps = (getProject().getName())
deps.libs.each { dep -> implementation 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
}
(对dependencies有疑惑的同学,我打个⼴告:)
然后你就可以在你的l中通过 右侧的click+to add arguments按钮添加safeArgs了
添加之后要怎么做呢?你需要build这个项⽬,这个时候android studio会⾃动帮你⽣成⼏个类。他的⽣成规则是:你原Fragment的名称+'Directions',⽐如你的framgnet名称是FragmentA 那他就会帮你⽣成⼀个叫做FragmentADirections的类,这个类⾥还有⼀个静态内部类,名字为你在xml中定义的Action,
如果你你这个fragment标签⾥有argument⼦标签(即配置过safeArg)。那么,这个静态内部类还会为你⽣成这些参数作为他的属性。这样你就可以愉快的传递参数了。传递参数的关键代码如下:
l:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论