-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
qianran
committed
May 4, 2020
1 parent
a452bd1
commit 9d179b9
Showing
3 changed files
with
209 additions
and
11 deletions.
There are no files selected for viewing
142 changes: 142 additions & 0 deletions
142
app/src/main/java/com/holiday/jetpackstudy/navigation/FixFragmentNavigator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
package com.holiday.jetpackstudy.navigation; | ||
|
||
import android.content.Context; | ||
import android.os.Bundle; | ||
import android.util.Log; | ||
import android.view.View; | ||
|
||
import androidx.annotation.IdRes; | ||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
import androidx.fragment.app.Fragment; | ||
import androidx.fragment.app.FragmentManager; | ||
import androidx.fragment.app.FragmentTransaction; | ||
import androidx.navigation.NavDestination; | ||
import androidx.navigation.NavOptions; | ||
import androidx.navigation.Navigator; | ||
import androidx.navigation.fragment.FragmentNavigator; | ||
|
||
import java.lang.reflect.Field; | ||
import java.util.ArrayDeque; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
/** | ||
* 支持fragment复用的导航器,搜"//fix"可快速定位到修复点 | ||
*/ | ||
@Navigator.Name("fixFragment") //fix 5: 需要指定1个名字,源码里自带的名字有navigation、activity、fragment、dialog | ||
public class FixFragmentNavigator extends FragmentNavigator { | ||
private static final String TAG = "FixFragmentNavigator"; | ||
private Context mContext; | ||
private FragmentManager mFragmentManager; | ||
private int mContainerId; | ||
|
||
public FixFragmentNavigator(@NonNull Context context, @NonNull FragmentManager manager, int containerId) { | ||
super(context, manager, containerId); | ||
mContext = context; | ||
mFragmentManager = manager; | ||
mContainerId = containerId; | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) { | ||
if (mFragmentManager.isStateSaved()) { | ||
Log.i(TAG, "Ignoring navigate() call: FragmentManager has already" | ||
+ " saved its state"); | ||
return null; | ||
} | ||
String className = destination.getClassName(); | ||
if (className.charAt(0) == '.') { | ||
className = mContext.getPackageName() + className; | ||
} | ||
//fix 1: 把类名作为tag,寻找已存在的Fragment | ||
//(如果想只针对个别fragment进行保活复用,可以在tag上做些标记比如加个前缀) | ||
Fragment frag = mFragmentManager.findFragmentByTag(className); | ||
if (null == frag) { | ||
//不存在,则创建 | ||
frag = instantiateFragment(mContext, mFragmentManager, className, args); | ||
} | ||
|
||
frag.setArguments(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); | ||
//fix 2: replace换成show和hide | ||
List<Fragment> fragments = mFragmentManager.getFragments(); | ||
for (Fragment fragment : fragments) { | ||
ft.hide(fragment); | ||
} | ||
if (!frag.isAdded()) { | ||
ft.add(mContainerId, frag, className); | ||
} | ||
ft.show(frag); | ||
ft.setPrimaryNavigationFragment(frag); | ||
|
||
final @IdRes int destId = destination.getId(); | ||
|
||
//fix 3: mBackStack是私有的,而且没有暴露出来,只能反射获取 | ||
ArrayDeque<Integer> mBackStack; | ||
try { | ||
Field field = FragmentNavigator.class.getDeclaredField("mBackStack"); | ||
field.setAccessible(true); | ||
mBackStack = (ArrayDeque<Integer>) field.get(this); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
return null; | ||
} | ||
|
||
final boolean initialNavigation = mBackStack.isEmpty(); | ||
final boolean isSingleTopReplacement = navOptions != null && !initialNavigation | ||
&& navOptions.shouldLaunchSingleTop() | ||
&& mBackStack.peekLast() == destId; | ||
|
||
boolean isAdded; | ||
if (initialNavigation) { | ||
isAdded = true; | ||
} else if (isSingleTopReplacement) { | ||
if (mBackStack.size() > 1) { | ||
mFragmentManager.popBackStack( | ||
generateBackStackName(mBackStack.size(), mBackStack.peekLast()), | ||
FragmentManager.POP_BACK_STACK_INCLUSIVE); | ||
ft.addToBackStack(generateBackStackName(mBackStack.size(), destId)); | ||
} | ||
isAdded = false; | ||
} else { | ||
ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId)); | ||
isAdded = true; | ||
} | ||
if (navigatorExtras instanceof Extras) { | ||
Extras extras = (Extras) navigatorExtras; | ||
for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) { | ||
ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue()); | ||
} | ||
} | ||
ft.setReorderingAllowed(true); | ||
ft.commit(); | ||
if (isAdded) { | ||
mBackStack.add(destId); | ||
return destination; | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
//fix 4: 从父类那边copy过来即可 | ||
@NonNull | ||
private String generateBackStackName(int backStackIndex, int destId) { | ||
return backStackIndex + "-" + destId; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters