定制的Fragment导航器,替换ft.replace(mCon
2020-05-15 本文已影响0人
Perston
1.对destination.json通过GsonFormat进行变成对象
2.编写AppConfig工具类对destination.json解释成HashMap
3.创建FixFragmentNavigator
4.创建NavGraphBuilder
1.对destination.json通过GsonFormat进行变成对象
image.png{
"main/tabs/home": {
"isFragment": true,
"asStarter": true,
"needLogin": false,
"pageUrl": "main/tabs/home",
"className": "com.mooc.ppjoke.ui.home.HomeFragment",
"id": 946010952
}
}
public class Destination {
/**
* isFragment : true
* asStarter : true
* needLogin : false
* pageUrl : main/tabs/home
* className : com.mooc.ppjoke.ui.home.HomeFragment
* id : 946010952
*/
public boolean isFragment;
public boolean asStarter;
public boolean needLogin;
public String pageUrl;
public String className;
public int id;
}
2.编写AppConfig工具类对destination.json解释成HashMap
import android.content.res.AssetManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.mooc.libcommon.global.AppGlobals;
import com.mooc.ppjoke.model.Destination;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
public class AppConfig {
private static HashMap<String, Destination> sDestConfig;
public static HashMap<String, Destination> getDestConfig() {
if (sDestConfig == null) {
String content = parseFile("destination.json");
sDestConfig = JSON.parseObject(content, new TypeReference<HashMap<String, Destination>>() {
});
}
return sDestConfig;
}
private static String parseFile(String fileName) {
AssetManager assets = AppGlobals.getApplication().getAssets();
InputStream is = null;
BufferedReader br = null;
StringBuilder builder = new StringBuilder();
try {
is = assets.open(fileName);
br = new BufferedReader(new InputStreamReader(is));
String line = null;
while ((line = br.readLine()) != null) {
builder.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (br != null) {
br.close();
}
} catch (Exception e) {
}
}
return builder.toString();
}
}
3.创建FixFragmentNavigator
/**
* 定制的Fragment导航器,替换ft.replace(mContainerId, frag);为 hide()/show()
*/
@Navigator.Name("fixfragment")
public class FixFragmentNavigator extends FragmentNavigator {
private static final String TAG = "FixFragmentNavigator";
private Context mContext;
private FragmentManager mManager;
private int mContainerId;
public FixFragmentNavigator(@NonNull Context context, @NonNull FragmentManager manager, int containerId) {
super(context, manager, containerId);
mContext = context;
mManager = manager;
mContainerId = containerId;
}
@Nullable
@Override
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
if (mManager.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;
}
//android.fragment.app.homefragment homefragment
String tag = className.substring(className.lastIndexOf(".") + 1);
Fragment frag = mManager.findFragmentByTag(tag);
if (frag == null) {
frag = instantiateFragment(mContext, mManager,
className, args);
}
frag.setArguments(args);
final FragmentTransaction ft = mManager.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);
}
List<Fragment> fragments = mManager.getFragments();
for (Fragment fragment : fragments) {
ft.hide(fragment);
}
if (!frag.isAdded()) {
ft.add(mContainerId, frag, tag);
}
ft.show(frag);
//ft.replace(mContainerId, frag);
ft.setPrimaryNavigationFragment(frag);
final @IdRes int destId = destination.getId();
ArrayDeque<Integer> mBackStack = null;
try {
Field field = FragmentNavigator.class.getDeclaredField("mBackStack");
field.setAccessible(true);
mBackStack = (ArrayDeque<Integer>) field.get(this);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
final boolean initialNavigation = mBackStack.isEmpty();
// TODO Build first class singleTop behavior for fragments
final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
&& navOptions.shouldLaunchSingleTop()
&& mBackStack.peekLast() == destId;
boolean isAdded;
if (initialNavigation) {
isAdded = true;
} else if (isSingleTopReplacement) {
// Single Top means we only want one instance on the back stack
if (mBackStack.size() > 1) {
// If the Fragment to be replaced is on the FragmentManager's
// back stack, a simple replace() isn't enough so we
// remove it from the back stack and put our replacement
// on the back stack in its place
mManager.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();
// The commit succeeded, update our view of the world
if (isAdded) {
mBackStack.add(destId);
return destination;
} else {
return null;
}
}
private String generateBackStackName(int backStackindex, int destid) {
return backStackindex + "-" + destid;
}
}
4.创建NavGraphBuilder
import android.content.ComponentName;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.navigation.ActivityNavigator;
import androidx.navigation.NavController;
import androidx.navigation.NavGraph;
import androidx.navigation.NavGraphNavigator;
import androidx.navigation.NavigatorProvider;
import androidx.navigation.fragment.FragmentNavigator;
import com.mooc.libcommon.global.AppGlobals;
import com.mooc.ppjoke.navigator.FixFragmentNavigator;
import com.mooc.ppjoke.model.Destination;
import java.util.HashMap;
import java.util.Iterator;
public class NavGraphBuilder {
public static void build(FragmentActivity activity, FragmentManager childFragmentManager, NavController controller, int containerId) {
NavigatorProvider provider = controller.getNavigatorProvider();
//NavGraphNavigator也是页面路由导航器的一种,只不过他比较特殊。
//它只为默认的展示页提供导航服务,但真正的跳转还是交给对应的navigator来完成的
NavGraph navGraph = new NavGraph(new NavGraphNavigator(provider));
//FragmentNavigator fragmentNavigator = provider.getNavigator(FragmentNavigator.class);
//fragment的导航此处使用我们定制的FixFragmentNavigator,底部Tab切换时 使用hide()/show(),而不是replace()
FixFragmentNavigator fragmentNavigator = new FixFragmentNavigator(activity, childFragmentManager, containerId);
provider.addNavigator(fragmentNavigator);
ActivityNavigator activityNavigator = provider.getNavigator(ActivityNavigator.class);
HashMap<String, Destination> destConfig = AppConfig.getDestConfig();
Iterator<Destination> iterator = destConfig.values().iterator();
while (iterator.hasNext()) {
Destination node = iterator.next();
if (node.isFragment) {
FixFragmentNavigator.Destination destination = fragmentNavigator.createDestination();
destination.setId(node.id);
destination.setClassName(node.className);
destination.addDeepLink(node.pageUrl);
navGraph.addDestination(destination);
} else {
ActivityNavigator.Destination destination = activityNavigator.createDestination();
destination.setId(node.id);
destination.setComponentName(new ComponentName(AppGlobals.getApplication().getPackageName(), node.className));
destination.addDeepLink(node.pageUrl);
navGraph.addDestination(destination);
}
//给APP页面导航结果图 设置一个默认的展示页的id
if (node.asStarter) {
navGraph.setStartDestination(node.id);
}
}
controller.setGraph(navGraph);
}
}