My goal is to have menu items that are in a Toolbar still be in that Toolbar when the app is closed and reopened (Activity destroyed and created). The menu items currently get removed when the app is closed and reopened. There are 0 items and I cannot access the items that were in the Toolbar; if I try I get the following error: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
The issue appears to be that onCreateOptionsMenu() is not being called when the app is restarted.
Here is the xml with the Toolbar:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:id="@+id/coordinatorLayoutActivityMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ActivityMain">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Here is the method I use to set up the Toolbar in onResume()
:
private void setUpActionBar() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setTitleTextColor(getResources().getColor(R.color.colorOnPrimary));
Drawable overflowIcon = toolbar.getOverflowIcon();
if (overflowIcon != null) {
overflowIcon.setColorFilter(getResources().getColor(R.color.colorOnPrimary), PorterDuff.Mode.SRC_ATOP);
}
setSupportActionBar(toolbar);
}
});
}
and onCreateOptionsMenu()
:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_toolbar, menu);
return true;
}
This correctly populates the Toolbar when the app is first opened.
My goal is to be able to run the following code without the IndexOutOfBoundsException
when a Fragment hosted in content_main has it's view created:
Toolbar toolbar = ((ActivityMain) getActivity()).findViewById(R.id.toolbar);
Menu menu = toolbar.getMenu();
menu.getItem(ActivityMain.MENU_ACTION_ADD).setVisible(true);
How can this be done?
The problem is that onCreateOptionsMenu()
isn't called until after a Fragment's View is created.
This means you need to set up a BroadcastReceiver with the Fragment when it's View is created and send out a matching Broadcast via an Intent at the end of onCreateOptionsMenu()
.
Here is an example.
First set up the BroadcastReceiver in onViewCreated()
. Shoutout to CommonsWare for making me aware of the race condition. Thank you!
private void setUpBroadcastReceiverServiceOnOptionsMenuCreated() {
ActivityMain activityMain = ((ActivityMain) getActivity());
IntentFilter filterComplete = new IntentFilter();
filterComplete.addCategory(Intent.CATEGORY_DEFAULT);
filterComplete.addAction(activityMain.getResources().getString(
R.string.broadcast_receiver_on_create_options_menu));
broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Toolbar toolbar = activityMain.findViewById(R.id.toolbar);
// Menu items have been added!!!
Menu menu = toolbar.getMenu();
menu.getItem(ActivityMain.MENU_ACTION_ADD).setVisible(true);
}
};
activityMain.registerReceiver(broadcastReceiver, filterComplete);
}
Create a method to send the Broadcast:
private void sendBroadcastOnOptionsMenuCreated() {
Intent intent = new Intent();
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setAction(getResources().getString(
R.string.broadcast_receiver_on_create_options_menu));
sendBroadcast(intent);
}
Finally, send the broadcast when the menu is created.
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_toolbar, menu);
sendBroadcastOnOptionsMenuCreated();
return true;
}