Android 导航架构组件-如何使用导航控制器设置/更改自定义背面或汉堡图标?
我正在尝试实现新引入的。就目前而言,它非常酷,对于管理应用程序的导航流非常有用 我已经在MainActivity中使用工具栏设置了基本导航,包括抽屉布局,如下所示:Android 导航架构组件-如何使用导航控制器设置/更改自定义背面或汉堡图标?,android,android-layout,navigation,android-architecture-components,android-jetpack,Android,Android Layout,Navigation,Android Architecture Components,Android Jetpack,我正在尝试实现新引入的。就目前而言,它非常酷,对于管理应用程序的导航流非常有用 我已经在MainActivity中使用工具栏设置了基本导航,包括抽屉布局,如下所示: class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navController = Navigation.findNavController(this, R.id.mainNavFragment)
// Set up ActionBar
setSupportActionBar(toolbar)
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
// Set up navigation menu
navigationView.setupWithNavController(navController)
}
override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(Navigation.findNavController(this, R.id.mainNavFragment), drawerLayout)
}
}
<item name="drawerArrowStyle">@style/ToolbarArrow</item>
使用此布局:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:navigationIcon="@drawable/ic_home_black_24dp"/>
</android.support.design.widget.AppBarLayout>
<fragment
android:id="@+id/mainNavFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_main"/>
</LinearLayout>
谢谢 使用导航版本
1.0.0-alpha07
时,我遇到了同样的问题。我发现了以下解决方法:
在我的活动中,我将工具栏视图设置为支持操作栏和导航控制器:
val toolbar = findViewById<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar)
toolbar.setupWithNavController(navController, appBarConfiguration)
当我这样做时,导航组件似乎仍然能够正确地处理backbackback、动画和删除开始片段上的图标。希望在离开alpha之前,导航组件支持自定义图标,而不必求助于老式的操作栏API。我在导航版本
1.0.0-alpha08
上也遇到了同样的问题。我通过重新实现setupActionBarWithNavController来解决这个问题,以提供我需要的自定义行为
setDisplayShowTitleEnabled(false) //Disable the default title
container.findNavController().addOnDestinationChangedListener { _, destination: NavDestination, _ ->
//Set the toolbar text (I've placed a TextView within the AppBar, which is being referenced here)
toolbar_text.text = destination.label
//Set home icon
supportActionBar?.apply {
setDisplayHomeAsUpEnabled((destination.id in NO_HOME_ICON_FRAGMENTS).not())
setHomeAsUpIndicator(if (destination.id in TOP_LEVEL_FRAGMENTS) R.drawable.app_close_ic else 0)
}
}
您只需将onDestinationChangedListener添加到导航控制器,并为每个目的地设置标题和图标
此代码应放在包含导航图的活动中的onCreate()方法中,并且应从代码中删除NavigationUI.setupActionBarWithNavController(This,navController,drawerLayout)
注意:这将删除导航抽屉的默认功能
我希望这有帮助 我最近也遇到了同样的问题。我想使用默认设置
NavigationUI.setupActionBarWithNavController(这个,navController,抽屉布局)
,也不想在每个片段中写入逻辑。这就是在项目中使用导航组件的用例。在挖掘导航组件的源代码后,我提出了以下解决方案:
findNavController(R.id.nav_host_fragment)
.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.nav_home,
R.id.nav_gallery -> {
drawerToggleDelegate!!
.setActionBarUpIndicator(
ContextCompat.getDrawable(
this,
R.drawable.custom_hamburger
),
androidx.navigation.ui.R.string.nav_app_bar_open_drawer_description
)
}
else -> {
drawerToggleDelegate!!
.setActionBarUpIndicator(
ContextCompat.getDrawable(
this,
R.drawable.custom_back
),
androidx.navigation.ui.R.string.nav_app_bar_navigate_up_description
)
}
}
}
我已将DestinationChangedListener
添加到navController
中,并使用appcompativity
的drawertogglegate
属性更改图标。这里,R.id.nav_home
和R.id.nav_gallery
是我的顶级目的地;其中,将显示自定义汉堡,对于其他汉堡,将显示自定义返回图标这种方法的唯一缺点是会丢失默认动画
但是您可以在这里很容易地实现您的逻辑,以获得动画效果。导航组件使用DrawerArrowDrawable作为底层实现,它实际上在汉堡图标和后箭头图标之间进行动画。可以通过样式在一定程度上修改两个图标的外观,请参见 对于我们的应用程序,我们使用以下属性获得一个细长箭头:
<style name="ToolbarArrow" parent="Widget.AppCompat.DrawerArrowToggle">
<item name="color">@color/primary</item>
<item name="drawableSize">32dp</item>
<item name="arrowShaftLength">22dp</item>
<item name="thickness">1.7dp</item>
<item name="arrowHeadLength">8dp</item>
</style>
@颜色/原色
32dp
22dp
1.7dp
8dp
必须在应用程序的主题中设置此样式,如下所示:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navController = Navigation.findNavController(this, R.id.mainNavFragment)
// Set up ActionBar
setSupportActionBar(toolbar)
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
// Set up navigation menu
navigationView.setupWithNavController(navController)
}
override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(Navigation.findNavController(this, R.id.mainNavFragment), drawerLayout)
}
}
<item name="drawerArrowStyle">@style/ToolbarArrow</item>
@style/toolbarrow
由于样式AppTheme使用样式Widget.AppCompat.drawerRowToggle作为导航UI汉堡和后退按钮图标,因此您需要通过继承Widget.AppCompat.drawerRowToggle来创建自定义样式
我将尝试用尽所有的可能的选项,以及每个选项的功能:
<style name="CustomNavigationIcons" parent="Widget.AppCompat.DrawerArrowToggle">
<!-- The drawing color for the bars -->
<item name="color">@color/colorGrey</item>
<!-- The total size of the drawable -->
<item name="drawableSize">64dp</item>
<!-- The thickness (stroke size) for the bar paint -->
<item name="thickness">3dp</item>
<!-- Whether bars should rotate or not during transition -->
<item name="spinBars">true</item>
<!-- The max gap between the bars when they are parallel to each other -->
<item name="gapBetweenBars">4dp</item>
<!-- The length of the bars when they are parallel to each other -->
<item name="barLength">27dp</item>
<!-- The length of the shaft when formed to make an arrow -->
<item name="arrowShaftLength">23dp</item>
<!-- The length of the arrow head when formed to make an arrow -->
<item name="arrowHeadLength">7dp</item>
</style>
@颜色/颜色灰
64dp
3dp
真的
4dp
27dp
23dp
7dp
最后,您必须确保AppTheme现在使用您的自定义样式作为DrawarRowToggle,如下所示:
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<!-- Other styles -->
<item name="drawerArrowStyle">@style/CustomNavigationIcons</item>
</style>
@颜色/原色
@颜色/原色暗
@颜色/颜色重音
@样式/自定义导航图标
我也面临同样的问题。我刚刚从SDK中重写了一些类:
class ToolbarOnDestinationChangedListener(toolbar: Toolbar) : OnDestinationChangedListener {
private val toolbarWeakReference = WeakReference(toolbar)
override fun onDestinationChanged(
controller: NavController,
destination: NavDestination,
arguments: Bundle?
) {
if (toolbarWeakReference.get() == null) {
controller.removeOnDestinationChangedListener(this)
return
}
if (destination is FloatingWindow) {
return
}
destination.label?.let {
val title = pickTitleFromArgs(it, arguments)
setTitle(title)
}
}
private fun pickTitleFromArgs(label: CharSequence, arguments: Bundle?): StringBuffer {
val title = StringBuffer()
val matcher = Pattern.compile("\\{(.+?)\\}").matcher(label)
while (matcher.find()) {
val argName = matcher.group(1)
if (arguments != null && arguments.containsKey(argName)) {
matcher.appendReplacement(title, "")
title.append(arguments[argName].toString())
} else {
error("Could not find $argName in $arguments to fill label $label")
}
}
matcher.appendTail(title)
return title
}
private fun setTitle(title: CharSequence) {
toolbarWeakReference.get()?.let {
it.title = title
}
}
}
以及以下扩展:
fun Toolbar.setNavController(navController: NavController) {
navController.addOnDestinationChangedListener(ToolbarOnDestinationChangedListener(this))
toolbar.setNavigationOnClickListener { navController.navigateUp() }
}
使用:
此代码不控制图标。但它考虑了标签、参数和目的地的变化。你应该用XML设置图标。事实上,我不知道这是谷歌的一个功能或一个bug 在给出更改自定义返回图标的解决方案之前。让我们看看谷歌是如何实现的 当我们呼叫时,一切开始:
val navController = findNavController(R.id.nav_host_fragment)
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.homeFragment,
R.id.categoryFragment,
R.id.cartFragment,
R.id.myAccountFragment,
R.id.moreFragment
),
)
toolbar.setupWithNavController(navController, appBarConfiguration)
setupWithNavController()是工具栏的扩展
fun Toolbar.setupWithNavController(
navController: NavController,
configuration: AppBarConfiguration = AppBarConfiguration(navController.graph)
) {
NavigationUI.setupWithNavController(this, navController, configuration)
}
在NavigationUI中,您可以看到Google只需监听目的地的更改,并在用户单击后退按钮时设置一个单击事件
public static void setupWithNavController(@NonNull Toolbar toolbar,
@NonNull final NavController navController,
@NonNull final AppBarConfiguration configuration) {
navController.addOnDestinationChangedListener(
new ToolbarOnDestinationChangedListener(toolbar, configuration));
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
navigateUp(navController, configuration);
}
});
}
更详细地说,您可以看到此功能:
private void setActionBarUpIndicator(boolean showAsDrawerIndicator) {
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);
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);
}
}
下面是setNavigationIcon:
@Override
protected void setNavigationIcon(Drawable icon,
@StringRes int contentDescription) {
Toolbar toolbar = mToolbarWeakReference.get();
if (toolbar != null) {
boolean useTransition = icon == null && toolbar.getNavigationIcon() != null;
toolbar.setNavigationIcon(icon);
toolbar.setNavigationContentDescription(contentDescription);
if (useTransition) {
TransitionManager.beginDelayedTransition(toolbar);
}
}
}
我只是看到每次目标改变时,目标都不是根页面。Google创建了一个DrawarRowDrawable对象,然后Google将此图标设置为导航图标。我认为这就是工具栏不显示自定义图标的原因
在这一点上,我认为我们有一些解决办法。
一个简单的解决方案是在Activity的onCreate()方法中添加addOnDestinationChangedListener()方法,每当用户更改到新页面(目的地)时,我们将检查并决定是否显示自定义返回图标。像这样:
val navController = findNavController(R.id.nav_host_fragment)
navController.addOnDestinationChangedListener { _, destination, _ ->
(appBarConfiguration.topLevelDestinations.contains(destination.id)) {
toolbar.navigationIcon = null
} else {
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white)
}
}
试试这个Hi@NILESHARTHOD,请阅读问题,图标正确显示。但我想用我自己的图标向量/png来解决itI问题。我认为这是最好的解决方案,现在应该被接受为一个正确的答案。我认为@bdemuth提供的解决方案是一个优雅的解决方案,而不是这个解决方案。这个解决方案不好。有时(但仅有时)您可以在看到自己的图标之前看到导航库用作其默认实现的抽屉行图标。很遗憾,这是不可接受的。您从哪里获得的
drawerToggleDelegate
?@AngelinadrawerToggleDelete
来自appcompativity
val navController = findNavController(R.id.nav_host_fragment)
navController.addOnDestinationChangedListener { _, destination, _ ->
(appBarConfiguration.topLevelDestinations.contains(destination.id)) {
toolbar.navigationIcon = null
} else {
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white)
}
}