I'm using
'com.google.android.material:material:1.12.0'
and
'androidx.appcompat:appcompat:1.7.0'
I have a fragment which contains a recycler view. When I select an item (long press) in the RecyclerView I enter ActionMode and show a different toolbar with actions specific to the chosen item(s)
I encountered an issue in Android 15, that when the app enters ActionMode, the status bar changes color from my expected blue to black
So I looked up various solutions here on SO for changing the status bar color in Android 15, and tried to implement them. My solution is something like this:
override fun onCreateActionMode(
mode: ActionMode?, menu: Menu?
): Boolean {
val inflater: MenuInflater = mode?.menuInflater ?: return true
inflater.inflate(R.menu.multi_selection_menu, menu)
multiSelectionMenu = menu
// Deactivate PullToRefresh
setPullToRefreshActivation(false)
// some other buttons hid...
val color = getColor(R.color.bar_background)
setStatusBarColor(color, true)
return true
}
override fun onDestroyActionMode(mode: ActionMode?) {
// Activate PullToRefresh
setPullToRefreshActivation(true)
// Show hidden buttons...
// finished multi selection
detectedTagsAdapter.apply {
disableMultiSelect()
clearSelectedItems()
notifyDataSetChanged()
}
val color = getColor(R.color.bar_background)
setStatusBarColor(color, false)
}
private fun setStatusBarColor(color: Int, on: Boolean) {
val window = requireActivity().window
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
window.decorView.setOnApplyWindowInsetsListener { view, insets ->
val statusBarInsets = insets.getInsets(WindowInsets.Type.statusBars())
view.setBackgroundColor(color)
// Adjust padding to avoid overlap
if (on) {
// view.setPadding(0, statusBarInsets.top, 0, 0)
} else {
view.setPadding(0, 0, 0, 0)
}
// insets.consumeSystemWindowInsets() // makes both bars appear
insets.consumeStableInsets() //leaves a black bar
}
} else {
window.statusBarColor = color
}
}
So - I have been tinkering with this to try to make it work.
If you see the commented lines in the function setStatusBarColor
it basically summarizes where I have got to:
insets
) then similarly the status bar goes black - but the toolobar changes to the ActionBarinsets.consumeStableInsets()
I still get a black status barinsets.consumeSystemWindowInsets()
then the status bar is overwritten by the the action bar (can't use the action bar because the status bar gets the touches) and the regular toolbar is still visibleinsets.consumeSystemWindowInsets()
I add in the code to put in the statusbarInserts top padding, then the status bar keeps its color (hooray!) BUT I now see BOTH the action bar AND then below it the original toolbar (which should be superseded by the action barThe documentation on this is extremely weak, can anyone explain why in Android 15 the ActionBar doesn't simply take the place of the regular toolbar? It works fine in Android 14 and below.
Any help would be appreciated.
I'm sure in principle I should move all my UI over to Compose, but this is a huge legacy project and I don't have the leisure to rewrite the entire UI just because one screen has a bug in Android 15
This was a problem for me as well couldn't figure it out. Finally found a way via a different Stackoverflow post.
appcompat-v7 v23.0.0 statusbar color black when in ActionMode
Here is what worked for mem put these in colors.xml
<color name="abc_decor_view_status_guard" tools:override="true">@color/colorAccent</color>
<color name="abc_decor_view_status_guard_light" tools:override="true">@color/colorAccent</color>
Update
Ok so while the above allows you to change the status bar color during Action Mode it does have some drawbacks, it causes the status bar color to change immediately while the toolbar fades in and out. It looks quite bad in my opinion. I worked up a better way to do this. It might be a bit hacky but I couldn't figure any other way to it. It does work and looks much better. The status bar and toolbar will now fade in and out at the same rate looking much more natural.
First put the two colors below in your colors.xml file, this will override the Action Bar status guard making it transparent instead of black.
<color name="abc_decor_view_status_guard" tools:override="true">@android:color/transparent</color>
<color name="abc_decor_view_status_guard_light" tools:override="true">@android:color/transparent</color>
Next add a View above your toolbar in xml, I called mine fake_status_guard, this will kind of be like your own status guard. Set the alpha to 0.0 so it normally cannot be seen when the activity is created.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<View
android:id="@+id/fake_status_guard"
android:layout_width="match_parent"
android:layout_height="?attr/collapsingToolbarLayoutMediumSize"
android:background="@color/colorRedButton"
android:alpha="0.0" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimaryDark"
app:navigationIcon="?attr/homeAsUpIndicator"
app:titleTextAppearance="@style/Text20AllerBold" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/app_bar" />
</RelativeLayout>
Then in your activity that is using Action Mode programmatically set the height of this view in onCreate to the height of the status bar via setOnApplyWindowInsetsListener like below
ViewCompat.setOnApplyWindowInsetsListener(binding.fakeStatusGuard, (v, windowInsets) -> {
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars());
v.getLayoutParams().height = insets.top;
v.setLayoutParams(v.getLayoutParams());
return windowInsets;
});
You will now need to fade the custom status guard in and out when Action Mode starts and stops. Add the below to your Activity that is using Action Mode
@Override
public void onActionModeStarted(ActionMode mode) {
super.onActionModeStarted(mode);
binding.fakeStatusGuard.animate().alpha(1f);
}
@Override
public void onActionModeFinished(ActionMode mode) {
super.onActionModeFinished(mode);
binding.fakeStatusGuard.animate().alpha(0f);
}
This fades the new view in and out along with the action bar making the animation look much better.