Android知识总结
一、使用 navigation 导航 Fragment 分析
1.1、Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val btn = view.findViewById
1.2、Navigation.findNavController
public static NavController findNavController(@NonNull View view) {
//根据 view 获取导航控制器
NavController navController = findViewNavController(view);
if (navController == null) {
throw new IllegalStateException("View " + view + " does not have a NavController set");
}
return navController;
}
private static NavController findViewNavController(@NonNull View view) {
while (view != null) {
NavController controller = getViewNavController(view);
if (controller != null) {
return controller;
}
ViewParent parent = view.getParent();
view = parent instanceof View ? (View) parent : null;
}
return null;
}
private static NavController getViewNavController(@NonNull View view) {
Object tag = view.getTag(R.id.nav_controller_view_tag);
NavController controller = null;
if (tag instanceof WeakReference) {
controller = ((WeakReference) tag).get();
} else if (tag instanceof NavController) {
controller = (NavController) tag;
}
return controller;
}
1.3、 NavController.navigation
public void navigate(@IdRes int resId) {
navigate(resId, null);
}
public void navigate(@IdRes int resId, @Nullable Bundle args) {
navigate(resId, args, null);
}
public void navigate(@IdRes int resId, @Nullable Bundle args,
@Nullable NavOptions navOptions) {
navigate(resId, args, navOptions, null);
}
final Deque mBackStack = new ArrayDeque<>();
public void navigate(@IdRes int resId, @Nullable Bundle args, @Nullable NavOptions navOptions,
@Nullable Navigator.Extras navigatorExtras) {
// 目的 是在 Graph里面去寻找 当前节点的Fragment 为 目的地
NavDestination currentNode = mBackStack.isEmpty() // 先去看看栈有没有
? mGraph
: mBackStack.getLast().getDestination(); // 如果栈没有,就取最后一个
if (currentNode == null) {
throw new IllegalStateException("no current navigation node");
}
@IdRes int destId = resId;
// 获取节点后,就要去获取 action 导航动作,只有action才能通过resId 获取下一个要导航的目标id
final NavAction navAction = currentNode.getAction(resId);
Bundle combinedArgs = null;
if (navAction != null) {
if (navOptions == null) {
navOptions = navAction.getNavOptions();
}
// 获取下一个目的地的 id号信息等
destId = navAction.getDestinationId();
Bundle navActionArgs = navAction.getDefaultArguments();
if (navActionArgs != null) {
combinedArgs = new Bundle();
combinedArgs.putAll(navActionArgs);
}
}
if (args != null) {
if (combinedArgs == null) {
combinedArgs = new Bundle();
}
combinedArgs.putAll(args);
}
if (destId == 0 && navOptions != null && navOptions.getPopUpTo() != -1) {
popBackStack(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive());
return;
}
if (destId == 0) {
throw new IllegalArgumentException("Destination id == 0 can only be used"
+ " in conjunction with a valid navOptions.popUpTo");
}
// 检查健壮性
NavDestination node = findDestination(destId);
if (node == null) {
final String dest = NavDestination.getDisplayName(mContext, destId);
if (navAction != null) {
throw new IllegalArgumentException("Navigation destination " + dest
+ " referenced from action "
+ NavDestination.getDisplayName(mContext, resId)
+ " cannot be found from the current destination " + currentNode);
} else {
throw new IllegalArgumentException("Navigation action/destination " + dest
+ " cannot be found from the current destination " + currentNode);
}
}
// 这行代码就是我们要关心的主线流程
navigate(node, combinedArgs, navOptions, navigatorExtras);
}
1.4、 NavController.navigation
private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
boolean popped = false;
boolean launchSingleTop = false;
if (navOptions != null) {
if (navOptions.getPopUpTo() != -1) {
popped = popBackStackInternal(navOptions.getPopUpTo(),
navOptions.isPopUpToInclusive());
}
}
//这里又回到了,最初开始的代码
Navigator navigator = mNavigatorProvider.getNavigator(
node.getNavigatorName());
Bundle finalArgs = node.addInDefaultArgs(args);
NavDestination newDest = navigator.navigate(node, finalArgs,
navOptions, navigatorExtras);
if (newDest != null) {
if (!(newDest instanceof FloatingWindow)) {
while (!mBackStack.isEmpty()
&& mBackStack.peekLast().getDestination() instanceof FloatingWindow
&& popBackStackInternal(
mBackStack.peekLast().getDestination().getId(), true)) {
// Keep popping
}
}
ArrayDeque hierarchy = new ArrayDeque<>();
NavDestination destination = newDest;
if (node instanceof NavGraph) {
do {
NavGraph parent = destination.getParent();
if (parent != null) {
NavBackStackEntry entry = new NavBackStackEntry(mContext, parent,
finalArgs, mLifecycleOwner, mViewModel);
hierarchy.addFirst(entry);
// Pop any orphaned copy of that navigation graph off the back stack
if (!mBackStack.isEmpty()
&& mBackStack.getLast().getDestination() == parent) {
popBackStackInternal(parent.getId(), true);
}
}
destination = parent;
} while (destination != null && destination != node);
}
// Now collect the set of all intermediate NavGraphs that need to be put onto
// the back stack
destination = hierarchy.isEmpty()
? newDest
: hierarchy.getFirst().getDestination();
while (destination != null && findDestination(destination.getId()) == null) {
NavGraph parent = destination.getParent();
if (parent != null) {
NavBackStackEntry entry = new NavBackStackEntry(mContext, parent, finalArgs,
mLifecycleOwner, mViewModel);
hierarchy.addFirst(entry);
}
destination = parent;
}
NavDestination overlappingDestination = hierarchy.isEmpty()
? newDest
: hierarchy.getLast().getDestination();
// Pop any orphaned navigation graphs that don't connect to the new destinations
//noinspection StatementWithEmptyBody
while (!mBackStack.isEmpty()
&& mBackStack.getLast().getDestination() instanceof NavGraph
&& ((NavGraph) mBackStack.getLast().getDestination()).findNode(
overlappingDestination.getId(), false) == null
&& popBackStackInternal(mBackStack.getLast().getDestination().getId(), true)) {
// Keep popping
}
mBackStack.addAll(hierarchy);
// The mGraph should always be on the back stack after you navigate()
if (mBackStack.isEmpty() || mBackStack.getFirst().getDestination() != mGraph) {
NavBackStackEntry entry = new NavBackStackEntry(mContext, mGraph, finalArgs,
mLifecycleOwner, mViewModel);
mBackStack.addFirst(entry); // 留意一下,每次都会管理 进栈
}
// And finally, add the new destination with its default args
NavBackStackEntry newBackStackEntry = new NavBackStackEntry(mContext, newDest,
newDest.addInDefaultArgs(finalArgs), mLifecycleOwner, mViewModel);
mBackStack.add(newBackStackEntry);
} else if (navOptions != null && navOptions.shouldLaunchSingleTop()) {
launchSingleTop = true;
NavBackStackEntry singleTopBackStackEntry = mBackStack.peekLast();
if (singleTopBackStackEntry != null) {
singleTopBackStackEntry.replaceArguments(finalArgs);
}
}
updateOnBackPressedCallbackEnabled();
if (popped || newDest != null || launchSingleTop) {
dispatchOnDestinationChanged();
}
}
接下来的分析建上节Navigation(一)FragmentNavigator
导航页面分析
二、绑定底部导航栏 BottomNavigationView
- 入口
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_navigation_main);
BottomNavigationView mBottomNavigationView = findViewById(R.id.nav_view);
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
NavController navController = navHostFragment.getNavController();
//绑定底部导航栏
NavigationUI.setupWithNavController(mBottomNavigationView, navController);
}
进入NavigationUI#setupWithNavController
方法
public static void setupWithNavController(
@NonNull final BottomNavigationView bottomNavigationView,
@NonNull final NavController navController) {
bottomNavigationView.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
//通过 item 获取导航
return onNavDestinationSelected(item, navController);
}
});
//底部导航进行弱引用防止内存泄露
final WeakReference weakReference =
new WeakReference<>(bottomNavigationView);
navController.addOnDestinationChangedListener(
new NavController.OnDestinationChangedListener() {
@Override
public void onDestinationChanged(@NonNull NavController controller,
@NonNull NavDestination destination, @Nullable Bundle arguments) {
BottomNavigationView view = weakReference.get();
if (view == null) {
navController.removeOnDestinationChangedListener(this);
return;
}
Menu menu = view.getMenu();
//遍历导航对应的item
for (int h = 0, size = menu.size(); h < size; h++) {
MenuItem item = menu.getItem(h);
//根据 id 判断fragment对应的 item
if (matchDestination(destination, item.getItemId())) {
//设置状态
item.setChecked(true);
}
}
}
});
}
- bottomNavigationView 监听流程
public static boolean onNavDestinationSelected(@NonNull MenuItem item,
@NonNull NavController navController) {
NavOptions.Builder builder = new NavOptions.Builder()
.setLaunchSingleTop(true);
//判断导航目是否是 ActivityNavigator 导航
if (navController.getCurrentDestination().getParent().findNode(item.getItemId())
instanceof ActivityNavigator.Destination) {
builder.setEnterAnim(R.anim.nav_default_enter_anim)
.setExitAnim(R.anim.nav_default_exit_anim)
.setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
.setPopExitAnim(R.anim.nav_default_pop_exit_anim);
} else {
builder.setEnterAnim(R.animator.nav_default_enter_anim)
.setExitAnim(R.animator.nav_default_exit_anim)
.setPopEnterAnim(R.animator.nav_default_pop_enter_anim)
.setPopExitAnim(R.animator.nav_default_pop_exit_anim);
}
if ((item.getOrder() & Menu.CATEGORY_SECONDARY) == 0) {
builder.setPopUpTo(findStartDestination(navController.getGraph()).getId(), false);
}
NavOptions options = builder.build();
try {
//调用 NavController#navigate 进行实际的导航,到指定的fragment
navController.navigate(item.getItemId(), null, options);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
NavController#getCurrentDestination
public NavDestination getCurrentDestination() {
NavBackStackEntry entry = getCurrentBackStackEntry();
return entry != null ? entry.getDestination() : null;
}
NavGraph#findNode
获取导航目的
final SparseArrayCompat mNodes = new SparseArrayCompat<>();
public final NavDestination findNode(@IdRes int resid) {
return findNode(resid, true);
}
final NavDestination findNode(@IdRes int resid, boolean searchParents) {
NavDestination destination = mNodes.get(resid);
// 返回导航目的
return destination != null
? destination
: searchParents && getParent() != null ? getParent().findNode(resid) : null;
}
- navController 监听流程
static boolean matchDestination(@NonNull NavDestination destination,
@IdRes int destId) {
NavDestination currentDestination = destination;
while (currentDestination.getId() != destId && currentDestination.getParent() != null) {
currentDestination = currentDestination.getParent();
}
return currentDestination.getId() == destId;
}