Android:根据深色/浅色主题动态切换ActionBar图标
Hey John, let's tackle your three questions about Android theme-aware icons one by one—these are all super common scenarios, so I’ve got practical solutions for you!
You’ve got a couple of solid options here, depending on whether you prefer XML-based configuration or code logic:
Option 1: Link icons to theme attributes
First, define a custom attribute inres/values/styles.xml:<attr name="actionBarIcon" format="reference" />Then, assign your black PNG to your light theme:
<style name="AppTheme.Light" parent="Theme.MaterialComponents.Light.NoActionBar"> <!-- Other theme attributes --> <item name="actionBarIcon">@drawable/ic_icon_black</item> </style>And the white PNG to your dark theme:
<style name="AppTheme.Dark" parent="Theme.MaterialComponents.Dark.NoActionBar"> <!-- Other theme attributes --> <item name="actionBarIcon">@drawable/ic_icon_white</item> </style>Finally, reference this attribute in your menu item or ActionBar setup:
<item android:id="@+id/action_my_icon" android:icon="?attr/actionBarIcon" android:title="@string/icon_title" app:showAsAction="always" />The system will automatically load the correct icon based on the active theme—no extra code needed.
Option 2: Switch icons via code
If you need to update icons manually (e.g., after the user toggles the theme at runtime), check the current UI mode and swap the drawable:val isDarkMode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES val targetIcon = if (isDarkMode) R.drawable.ic_icon_white else R.drawable.ic_icon_black // Update ActionBar home indicator supportActionBar?.setHomeAsUpIndicator(targetIcon) // Update a menu item menu.findItem(R.id.action_my_icon).setIcon(targetIcon)Just make sure to call this code after the theme is switched to refresh the icons.
Vector icons (VectorDrawable) are perfect for this—they let you avoid multiple icon files entirely. Here are two easy methods:
Method 1: Use theme color attributes for tint
First, define a color attribute inres/values/styles.xml:<attr name="iconTintColor" format="color" />Set it to black in your light theme:
<item name="iconTintColor">@color/black</item>And white in your dark theme:
<item name="iconTintColor">@color/white</item>Then apply this tint to your vector icon in the menu or layout:
<item android:id="@+id/action_my_icon" android:icon="@drawable/ic_vector_icon" android:title="@string/icon_title" app:iconTint="?attr/iconTintColor" app:showAsAction="always" />The vector icon will automatically inherit the tint color from the active theme—super clean!
Method 2: Dynamically tint via code
If you need to adjust the tint at runtime, useDrawableCompatfor backward compatibility:val iconDrawable = ContextCompat.getDrawable(this, R.drawable.ic_vector_icon) val tintColor = getThemeColor(this, R.attr.iconTintColor) DrawableCompat.setTint(iconDrawable, tintColor) menu.findItem(R.id.action_my_icon).icon = iconDrawableHere’s a helper function to fetch the theme color:
private fun getThemeColor(context: Context, attrRes: Int): Int { val typedValue = TypedValue() context.theme.resolveAttribute(attrRes, typedValue, true) return typedValue.data }
In almost all real-world scenarios, the performance impact is negligible—vector icons even offer some advantages over PNGs:
- Performance upsides:
- Smaller APK size: You only need one vector file instead of multiple PNGs for different screen densities.
- No scaling artifacts: Vectors scale perfectly to any screen size, so you don’t have to worry about blurry icons on high-res displays.
- Minor considerations:
- Extremely complex vector icons (with hundreds of paths or gradients) might take a tiny bit longer to draw than simple PNGs, but Material Icons are designed to be lightweight, so this isn’t an issue for standard use cases.
- For Android 5.0 (API 21) and below, you need to enable vector support with
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true), but since almost no devices run these old versions anymore, this is rarely a concern. - If you’re using a lot of vector icons in a RecyclerView, you can cache drawables to avoid redundant creation—but this is an edge-case optimization, not a requirement for most apps.
内容的提问来源于stack exchange,提问作者John

