I'm new to Android development, so I'm worried that my confusion may come from a misunderstanding about the ecosystem as a whole, not the implementation. I have a ListView
and want the behavior so that when I tap and hold on one of the items in the list, the Context Action Bar appears at the top and covers the androidx.appcompat.widget.Toolbar
I'm using as my ActionBar, rather than pushing the entire window down (including the item I'm tapping and holding).
I've read just about every question on here I could find regarding it (Can't show actionMode on top of Toolbar in any way, Toolbar and Contextual ActionBar with AppCompat-v7, Overlaying the Action Bar not working, Contextual action bar does not overlay my toolbar, How to make contextual actionmode bar overlay my layout instead of "pushing" it down), all of which basically have the same answer -- that you need to use <item name="windowActionModeOverlay">true</item>
.
I have that, and have tried a number of different things, but it just won't work. I reduced it to a minimal reproducible example, so I can include basically the entire project:
activity_main.xml
:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/main_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_constraintTop_toTopOf="parent"/>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/file_list"
android:longClickable="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/main_toolbar"
app:layout_constraintBottom_toBottomOf="parent"
android:paddingTop="?attr/actionBarSize"/>
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java
:
package com.example.minimalexample;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.Toolbar;
import java.util.Arrays;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private Toolbar actionBar;
private ListView listView;
private ActionMode actionMode;
private List<String> list = Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P");
private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate a menu resource providing context menu items.
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; }
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return true; }
@Override
public void onDestroyActionMode(ActionMode mode) { actionMode = null; }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
// Setup the Action Bar.
actionBar = findViewById(R.id.main_toolbar);
setSupportActionBar(actionBar);
listView = findViewById(R.id.file_list);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setAdapter(new ArrayAdapter<>(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, list));
listView.setOnItemLongClickListener((parent, view, position, id) -> {
if (actionMode != null) {
return false;
}
actionMode = startSupportActionMode(actionModeCallback);
return true;
});
}
}
themes.xml
:
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.MinimalExample" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
</style>
<style name="Theme.MinimalExample" parent="Base.Theme.MinimalExample">
<item name="android:windowActionModeOverlay">true</item>
<item name="android:windowActionBarOverlay">true</item>
<item name="android:windowActionBar">true</item>
<item name="windowActionModeOverlay">true</item>
<item name="windowActionBarOverlay">true</item>
<item name="windowActionBar">true</item>
</style>
</resources>
AndroidManifest.xml
:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MinimalExample"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.MinimalExample">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
All of this results in the following when I tap and hold one of the list items:
As you can see, the white Contextual Action Bar is above the ToolBar, not covering it like I want it to.
I also tried using AbsListView.MultiChoiceModeListener
instead of ActionMode.Callback
, but that had the same behavior. I think that might have a different issue, too, because MultiChoiceModeListener
does not have anywhere to call startSupportActionMode
. Also, it requires the older version of ActionMode
(in the android
namespace) which, if my understanding is right, would conflict with the ToolBar
in the androidx
namespace that I use and not allow them to work together.
Regardless, this doesn't work no matter what I do. Any ideas?
Turns out the solution was to have the windowActionModeOverlay
declaration in the base <style>
block. E.g., having just the following as my theme.xml
works:
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.MinimalExample" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<item name="windowActionModeOverlay">true</item>
</style>
<style name="Theme.MinimalExample" parent="Base.Theme.MinimalExample">
<!-- Customize your theme here. -->
</style>
</resources>
Frustratingly simple, but happy to have it solved.