Navigation和Toolbar配合使用时更改Toolbar返回按钮的颜色
方法
在styles.xml中增加一个样式
<style name="DrawerArrowStyle" parent="@style/Widget.AppCompat.DrawerArrowToggle">
<item name="color">#ffffff</item>
</style>
在当前的主题中增加drawerArrowStyle属性,该属性引用上面的样式。
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="color">@color/colorWhite</item>
<item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
</style>
原理
分析的入口点,Toolbar上面的扩展方法setupWithNavController()
val binding = ActivityNavigationUiEntryBinding.inflate(layoutInflater)
binding.toolbar.setupWithNavController(navController, appBarConfiguration)
fun Toolbar.setupWithNavController(
navController: NavController,
configuration: AppBarConfiguration = AppBarConfiguration(navController.graph)
) {
NavigationUI.setupWithNavController(this, navController, configuration)
}
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);
}
});
}
ToolbarOnDestinationChangedListener(
@NonNull Toolbar toolbar, @NonNull AppBarConfiguration configuration) {
super(toolbar.getContext(), configuration);
mToolbarWeakReference = new WeakReference<>(toolbar);
}
@Override
public void onDestinationChanged(@NonNull NavController controller,
@NonNull NavDestination destination, @Nullable Bundle arguments) {
Toolbar toolbar = mToolbarWeakReference.get();
if (toolbar == null) {
controller.removeOnDestinationChangedListener(this);
return;
}
super.onDestinationChanged(controller, destination, arguments);
}
ToolbarOnDestinationChangedListener的父类是AbstractAppBarOnDestinationChangedListener,super.onDestinationChanged(controller, destination, arguments);的代码如下:
@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;
}
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, 0);
} else {
setActionBarUpIndicator(openableLayout != null && isTopLevelDestination);
}
}
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);
}
}
由DrawerArrowDrawable来充当导航按钮的图标的
public DrawerArrowDrawable(Context context) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.MITER);
mPaint.setStrokeCap(Paint.Cap.BUTT);
mPaint.setAntiAlias(true);
final TypedArray a = context.getTheme().obtainStyledAttributes(null,
R.styleable.DrawerArrowToggle, R.attr.drawerArrowStyle,
R.style.Base_Widget_AppCompat_DrawerArrowToggle);
setColor(a.getColor(R.styleable.DrawerArrowToggle_color, 0));
setBarThickness(a.getDimension(R.styleable.DrawerArrowToggle_thickness, 0));
setSpinEnabled(a.getBoolean(R.styleable.DrawerArrowToggle_spinBars, true));
// round this because having this floating may cause bad measurements
setGapSize(Math.round(a.getDimension(R.styleable.DrawerArrowToggle_gapBetweenBars, 0)));
mSize = a.getDimensionPixelSize(R.styleable.DrawerArrowToggle_drawableSize, 0);
// round this because having this floating may cause bad measurements
mBarLength = Math.round(a.getDimension(R.styleable.DrawerArrowToggle_barLength, 0));
// round this because having this floating may cause bad measurements
mArrowHeadLength = Math.round(a.getDimension(
R.styleable.DrawerArrowToggle_arrowHeadLength, 0));
mArrowShaftLength = a.getDimension(R.styleable.DrawerArrowToggle_arrowShaftLength, 0);
a.recycle();
}
从当前主题中寻找R.attr.drawerArrowStyle属性,该属性必须是reference类型,引用一个样式。找不到的话,会到Base_Widget_AppCompat_DrawerArrowToggle这个样式中寻找
例子
假设app的主题是AppTheme,整个主题样式的继承关系如下:
Theme.Light
android:Theme.Holo.Light
Platform.AppCompat.Light
Base.V7.Theme.AppCompat.Light
Base.Theme.AppCompat.Light
Theme.AppCompat.Light
Theme.AppCompat.Light.NoActionBar
AppTheme
<style name="Base.V7.Theme.AppCompat.Light" parent="Platform.AppCompat.Light">
<item name="drawerArrowStyle">@style/Widget.AppCompat.DrawerArrowToggle</item>
</style>
<style name="Widget.AppCompat.DrawerArrowToggle" parent="Base.Widget.AppCompat.DrawerArrowToggle">
<item name="color">?attr/colorControlNormal</item>
</style>
<style name="Base.V7.Theme.AppCompat.Light" parent="Platform.AppCompat.Light">
<item name="colorControlNormal">?android:attr/textColorSecondary</item>
</style>
<style name="Platform.AppCompat.Light" parent="android:Theme.Holo.Light">
<item name="android:textColorSecondary">@color/abc_secondary_text_material_light</item>
</style>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="@color/secondary_text_disabled_material_light"/>
<item android:color="@color/secondary_text_default_material_light"/>
</selector>
<color name="secondary_text_default_material_light">#8a000000</color>