如何解决使用导航组件,底部导航菜单在 android 工具栏中自定义返回图标
我想在使用导航组件和底部导航菜单时在工具栏中使用我自己的后退图标。
当我写
binding.toolbar.setNavigationIcon(R.drawable.ic_arrow_back)
在 onCreate MainActivity 然后显示该图标,但是当我导航到下一个屏幕时,它使用默认值。
我添加了 onDestinationListener
val destinationChangedListener =
NavController.OnDestinationChangedListener { controller,destination,arguments ->
when(destination.id){
R.id.subCategoriesFragment -> {
// nothing is working
supportActionBar?.setHomeButtonEnabled(true)
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back)
binding.toolbar.setNavigationIcon(R.drawable.ic_arrow_back)
}
else -> {
binding.toolbar.navigationIcon = null
}
}
}
扩展代码如下。
/**
* Manages the varIoUs graphs needed for a [BottomNavigationView].
*
* This sample is a workaround until the Navigation Component supports multiple back stacks.
*/
fun BottomNavigationView.setupWithNavController(
navGraphIds: List<Int>,fragmentManager: FragmentManager,containerId: Int,intent: Intent
): LiveData<NavController> {
// Map of tags
val graphIdToTagMap = SparseArray<String>()
// Result. Mutable live data with the selected controlled
val selectednavController = mutablelivedata<NavController>()
var firstFragmentGraphId = 0
// First create a NavHostFragment for each NavGraph ID
navGraphIds.forEachIndexed { index,navGraphId ->
val fragmentTag = getFragmentTag(index)
// Find or create the Navigation host fragment
val navHostFragment = obtainNavHostFragment(
fragmentManager,fragmentTag,navGraphId,containerId
)
// Obtain its id
val graphId = navHostFragment.navController.graph.id
if (index == 0) {
firstFragmentGraphId = graphId
}
// Save to the map
graphIdToTagMap[graphId] = fragmentTag
// Attach or detach nav host fragment depending on whether it's the selected item.
if (this.selectedItemId == graphId) {
// Update livedata with the selected graph
selectednavController.value = navHostFragment.navController
attachNavHostFragment(fragmentManager,navHostFragment,index == 0)
} else {
detachNavHostFragment(fragmentManager,navHostFragment)
}
}
// Now connect selecting an item with swapping Fragments
var selectedItemTag = graphIdToTagMap[this.selectedItemId]
val firstFragmentTag = graphIdToTagMap[firstFragmentGraphId]
var isOnFirstFragment = selectedItemTag == firstFragmentTag
// When a navigation item is selected
setonNavigationItemSelectedListener { item ->
LocalbroadcastManager.getInstance(context).sendbroadcast(Intent(SELECTED_RESELETED_ITEM).apply {
putExtra("item",item.itemId)
})
// Don't do anything if the state is state has already been saved.
if (fragmentManager.isstateSaved) {
false
} else {
val newlySelectedItemTag = graphIdToTagMap[item.itemId]
if (selectedItemTag != newlySelectedItemTag) {
// Pop everything above the first fragment (the "fixed start destination")
fragmentManager.popBackStack(firstFragmentTag,FragmentManager.POP_BACK_STACK_INCLUSIVE)
val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag)
as NavHostFragment
// Exclude the first fragment tag because it's always in the back stack.
if (firstFragmentTag != newlySelectedItemTag) {
// Commit a transaction that cleans the back stack and adds the first fragment
// to it,creating the fixed started destination.
fragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.nav_default_enter_anim,R.anim.nav_default_exit_anim,R.anim.nav_default_pop_enter_anim,R.anim.nav_default_pop_exit_anim)
.attach(selectedFragment)
.setPrimaryNavigationFragment(selectedFragment)
.apply {
// Detach all other Fragments
graphIdToTagMap.forEach { _,fragmentTagIter ->
if (fragmentTagIter != newlySelectedItemTag) {
detach(fragmentManager.findFragmentByTag(firstFragmentTag)!!)
}
}
}
.addToBackStack(firstFragmentTag)
.setReorderingallowed(true)
.commit()
}
selectedItemTag = newlySelectedItemTag
isOnFirstFragment = selectedItemTag == firstFragmentTag
selectednavController.value = selectedFragment.navController
true
} else {
false
}
}
}
// Optional: on item reselected,pop back stack to the destination of the graph
setupItemReselected(graphIdToTagMap,fragmentManager)
// Handle deep link
setupDeepLinks(navGraphIds,fragmentManager,containerId,intent)
// Finally,ensure that we update our BottomNavigationView when the back stack changes
fragmentManager.addOnBackStackChangedListener {
if (!isOnFirstFragment && !fragmentManager.isOnBackStack(firstFragmentTag)) {
this.selectedItemId = firstFragmentGraphId
}
// Reset the graph if the currentDestination is not valid (happens when the back
// stack is popped after using the back button).
selectednavController.value?.let { controller ->
if (controller.currentDestination == null) {
controller.navigate(controller.graph.id)
}
}
}
return selectednavController
}
private fun BottomNavigationView.setupDeepLinks(
navGraphIds: List<Int>,intent: Intent
) {
navGraphIds.forEachIndexed { index,containerId
)
// Handle Intent
if (navHostFragment.navController.handleDeepLink(intent)
&& selectedItemId != navHostFragment.navController.graph.id) {
this.selectedItemId = navHostFragment.navController.graph.id
}
}
}
private fun BottomNavigationView.setupItemReselected(
graphIdToTagMap: SparseArray<String>,fragmentManager: FragmentManager
) {
setonNavigationItemReselectedListener { item ->
LocalbroadcastManager.getInstance(context).sendbroadcast(Intent(SELECTED_RESELETED_ITEM).apply {
putExtra("item",item.itemId)
})
val newlySelectedItemTag = graphIdToTagMap[item.itemId]
val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag)
as NavHostFragment
val navController = selectedFragment.navController
// Pop the back stack to the start destination of the current navController graph
navController.popBackStack(
navController.graph.startDestination,false
)
}
}
private fun detachNavHostFragment(
fragmentManager: FragmentManager,navHostFragment: NavHostFragment
) {
fragmentManager.beginTransaction()
.detach(navHostFragment)
.commitNow()
}
private fun attachNavHostFragment(
fragmentManager: FragmentManager,navHostFragment: NavHostFragment,isPrimaryNavFragment: Boolean
) {
fragmentManager.beginTransaction()
.attach(navHostFragment)
.apply {
if (isPrimaryNavFragment) {
setPrimaryNavigationFragment(navHostFragment)
}
}
.commitNow()
}
private fun obtainNavHostFragment(
fragmentManager: FragmentManager,fragmentTag: String,navGraphId: Int,containerId: Int
): NavHostFragment {
// If the Nav Host fragment exists,return it
val existingFragment = fragmentManager.findFragmentByTag(fragmentTag) as NavHostFragment?
existingFragment?.let { return it }
// Otherwise,create it and return it.
val navHostFragment = NavHostFragment.create(navGraphId)
fragmentManager.beginTransaction()
.add(containerId,fragmentTag)
.commitNow()
return navHostFragment
}
private fun FragmentManager.isOnBackStack(backStackName: String): Boolean {
val backStackCount = backStackEntryCount
for (index in 0 until backStackCount) {
if (getBackStackEntryAt(index).name == backStackName) {
return true
}
}
return false
}
private fun getFragmentTag(index: Int) = "bottomNavigation#$index"
任何人都可以帮忙,我如何使用自己的后退图标。
解决方法
导航组件的开发人员没有提供更改图标的方法。
但这可以通过以下方式实现:
- 在您的项目中创建一个包:“androidx.navigation.ui”
- 创建 ModifiedAbstractAppBarOnDestinationChangedListener:
package androidx.navigation.ui;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.graphics.drawable.DrawerArrowDrawable;
import androidx.customview.widget.Openable;
import androidx.navigation.FloatingWindow;
import androidx.navigation.NavController;
import androidx.navigation.NavDestination;
import java.lang.ref.WeakReference;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* The abstract OnDestinationChangedListener for keeping any type of app bar updated.
* This handles both updating the title and updating the Up Indicator,transitioning between
* the drawer icon and up arrow as needed.
* @hide
*/
public abstract class ModifiedAbstractAppBarOnDestinationChangedListener
implements NavController.OnDestinationChangedListener {
private final Context mContext;
private final Set<Integer> mTopLevelDestinations;
@Nullable
private final WeakReference<Openable> mOpenableLayoutWeakReference;
private DrawerArrowDrawable mArrowDrawable;
private ValueAnimator mAnimator;
ModifiedAbstractAppBarOnDestinationChangedListener(@NonNull Context context,@NonNull AppBarConfiguration configuration) {
mContext = context;
mTopLevelDestinations = configuration.getTopLevelDestinations();
Openable openableLayout = configuration.getOpenableLayout();
if (openableLayout != null) {
mOpenableLayoutWeakReference = new WeakReference<>(openableLayout);
} else {
mOpenableLayoutWeakReference = null;
}
}
protected abstract void setTitle(CharSequence title);
protected abstract void setNavigationIcon(Drawable icon,@StringRes int contentDescription,int destinationId);
@Override
public void onDestinationChanged(@NonNull NavController controller,@NonNull NavDestination destination,@Nullable Bundle arguments) {
if (destination instanceof FloatingWindow) {
return;
}
Openable openableLayout = mOpenableLayoutWeakReference != null
? mOpenableLayoutWeakReference.get()
: null;
if (mOpenableLayoutWeakReference != null && openableLayout == null) {
controller.removeOnDestinationChangedListener(this);
return;
}
int id = destination.getId();
CharSequence label = destination.getLabel();
if (label != null) {
// Fill in the data pattern with the args to build a valid URI
StringBuffer title = new StringBuffer();
Pattern fillInPattern = Pattern.compile("\\{(.+?)\\}");
Matcher matcher = fillInPattern.matcher(label);
while (matcher.find()) {
String argName = matcher.group(1);
if (arguments != null && arguments.containsKey(argName)) {
matcher.appendReplacement(title,"");
//noinspection ConstantConditions
title.append(arguments.get(argName).toString());
} else {
throw new IllegalArgumentException("Could not find " + argName + " in "
+ arguments + " to fill label " + label);
}
}
matcher.appendTail(title);
setTitle(title);
}
boolean isTopLevelDestination = NavigationUI.matchDestinations(destination,mTopLevelDestinations);
if (openableLayout == null && isTopLevelDestination) {
setNavigationIcon(null,id);
} else {
setActionBarUpIndicator(openableLayout != null && isTopLevelDestination,id);
}
}
private void setActionBarUpIndicator(boolean showAsDrawerIndicator,int destinationId) {
boolean animate = true;
if (mArrowDrawable == null) {
mArrowDrawable = new DrawerArrowDrawable(mContext);
// We're setting the initial state,so skip the animation
animate = false;
}
setNavigationIcon(mArrowDrawable,showAsDrawerIndicator
? R.string.nav_app_bar_open_drawer_description
: R.string.nav_app_bar_navigate_up_description,destinationId);
float endValue = showAsDrawerIndicator ? 0f : 1f;
if (animate) {
float startValue = mArrowDrawable.getProgress();
if (mAnimator != null) {
mAnimator.cancel();
}
mAnimator = ObjectAnimator.ofFloat(mArrowDrawable,"progress",startValue,endValue);
mAnimator.start();
} else {
mArrowDrawable.setProgress(endValue);
}
}
}
- 创建 ModifiedToolbarOnDestinationChangedListener 并添加使用 getModifiedIcon(drawable,destinationId) 方法更改或替换可绘制图标的功能:
package androidx.navigation.ui;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.Toolbar;
import androidx.navigation.NavController;
import androidx.navigation.NavDestination;
import androidx.transition.TransitionManager;
import java.lang.ref.WeakReference;
/**
* The OnDestinationChangedListener specifically for keeping a Toolbar updated.
* This handles both updating the title and updating the Up Indicator,transitioning between
* the drawer icon and up arrow as needed.
* @hide
*/
public class ModifiedToolbarOnDestinationChangedListener extends
ModifiedAbstractAppBarOnDestinationChangedListener {
private final WeakReference<Toolbar> mToolbarWeakReference;
public ModifiedToolbarOnDestinationChangedListener(
@NonNull Toolbar toolbar,@NonNull AppBarConfiguration configuration) {
super(toolbar.getContext(),configuration);
mToolbarWeakReference = new WeakReference<>(toolbar);
}
@Override
public void onDestinationChanged(@NonNull NavController controller,@Nullable Bundle arguments) {
Toolbar toolbar = mToolbarWeakReference.get();
if (toolbar == null) {
controller.removeOnDestinationChangedListener(this);
return;
}
super.onDestinationChanged(controller,destination,arguments);
}
@Override
protected void setTitle(CharSequence title) {
mToolbarWeakReference.get().setTitle(title);
}
@Override
protected void setNavigationIcon(Drawable icon,int destinationId) {
Toolbar toolbar = mToolbarWeakReference.get();
if (toolbar != null) {
Drawable modifiedIcon = getModifiedIcon(icon,destinationId);
boolean useTransition = modifiedIcon == null && toolbar.getNavigationIcon() != null;
toolbar.setNavigationIcon(modifiedIcon);
toolbar.setNavigationContentDescription(contentDescription);
if (useTransition) {
TransitionManager.beginDelayedTransition(toolbar);
}
}
}
@Nullable
protected Drawable getModifiedIcon(@Nullable Drawable drawable,int destinationId) {
return drawable;
}
}
- 使用修改后的类设置导航组件并根据destinationId更改可绘制图标:
fun setupWithNavController(toolbar: Toolbar,navController: NavController,configuration: AppBarConfiguration) {
val listener = object : ModifiedToolbarOnDestinationChangedListener(toolbar,configuration) {
override fun getModifiedIcon(drawable: Drawable?,destinationId: Int): Drawable? {
// Return the drawable in depend on destinationId
if (drawable is DrawerArrowDrawable){
drawable.color = Icon.getInstance().iconsColor
}
return drawable
}
}
navController.addOnDestinationChangedListener(listener)
toolbar.setNavigationOnClickListener { NavigationUI.navigateUp(navController,configuration) }
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。