I have a button in my OptionMenu which when touched will open a popup menu of items which are fetched at run time and added programmatically (so can't use hard coded xml menu items). I want to highlight a subset of these items according to their value, I used what is suggested here: How to customize item background and item text color inside NavigationView? to try and get the items to be colored differently. However, all of the items are colored the same despite the isChecked()
value being different.
Here is a small working example of the issue:
MainActivity.java
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.actionbar_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch(id) {
case R.id.action_bar_button:{
showMenu();
return true;
}
}
return super.onOptionsItemSelected(item);
}
private void showMenu() {
View v = findViewById(R.id.action_bar_button);
Context wrapper = new ContextThemeWrapper(this, R.style.MyPopupMenu);
PopupMenu popupMenu = new PopupMenu(wrapper, v);
//Sample items to demonstrate the issue. I want the background to be red if false, blue if true
Map<String, Boolean> list = new HashMap<>();
list.put("Item 1", true);
list.put("Item 2", false);
list.put("Item 3", true);
for(Map.Entry<String, Boolean> entry : list.entrySet()){
String msg = entry.getKey();
MenuItem item = popupMenu.getMenu().add(msg).setCheckable(true).setChecked(entry.getValue());
System.out.println(item.getTitle() + ": " + item.isChecked());
}
popupMenu.show();
}
styles.xml contains:
<style name="MyPopupMenu" parent="Widget.AppCompat.PopupMenu">
<item name="android:itemBackground">@drawable/menu_item_background</item>
</style>
actionbar_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_bar_button"
android:title="List"
android:icon="@drawable/ic_launcher_foreground"
app:showAsAction="always" />
</menu>
colors.xml contains:<color name="red">#ff0000</color>
<color name="blue">#0000FF</color>
And the menu_item_background.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/blue" android:state_checked="true"/>
<item android:drawable="@color/red" android:state_checked="false"/>
</selector>
However, when I run this I get the following:
As you can see although the isChecked
state of Item 1 and 3 is true, they still appear red. The logcat output confirms this.
As an experiment I changed the menu_item_background.xml
to use the android:state_enabled
instead of checked and it works as expected:
What's going on here? Why doesn't this work with android:state_checked
?
Thanks for your help.
If your main motive is to highlight the selected item or similar kind of problem.
I would like to suggest as one of the solutions which worked for me to use spinner, which Popup Menu provides a similar dropdown option as Spinner, with a custom layout for dropdown, and change the background color at getDropDownView()
at the adapter for the spinner.
R.layout.spinner_item.xml
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_context"
android:layout_height="wrap_content"
android:visibility="gone"
/>
R.layout.spinner_drop_down.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
>
<TextView
style="?android:attr/spinnerDropDownItemStyle"
android:singleLine="true"
android:layout_width="wrap_content"
android:layout_height="?attr/dropdownListPreferredItemHeight"
android:id="@+id/spinner_dropdown_text"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"/>
</LinearLayout>
UI Design
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<!--Some Layouts-->
<Spinner
android:id="@+id/spinner"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:background="@drawable/icon"
android:gravity="center"
android:textDirection="firstStrongLtr"
android:overlapAnchor="false"/>
<!--Some More Layouts-->
</LinearLayout>
MainActivity.kt
val spinner = findViewById<Spinner>(R.id.spinner)
val spinnerData = arrayListOf<String>("Item 1", "Item 2", "Item 3")
val arrayAdapter = object : ArrayAdapter<String>(activity!!.applicationContext,R.layout.spinner_item, spinnerData) {
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
val spinnerItem = layoutInflater.inflate(R.layout.spinner_drop_down, null)
val dropDownText = spinnerItem.findViewById<TextView>(R.id.spinner_dropdown_text)
dropDownText.text = spinnerData[position]
val selected = spinner.selectedItemPosition
if (position == selected) spinnerItem.setBackgroundColor(Color.GRAY)
return spinnerItem
}
}
arrayAdapter.setDropDownViewResource(R.layout.spinner_drop_down)
spinner.adapter = arrayAdapter
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
//Do whatever you want to do when Item selected
}
override fun onNothingSelected(parent: AdapterView<*>) {
//Do whatever you want to do when No Item selected
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With