so I am trying to get my menu item, that is show on the action bar to behave like a checkable menu option. The firs part works, meaning it is checkable and when I press it, and set in code the setChecked(true) it works. But what does not work is the visual part. There is no change in how a menu item looks on the action bar in checked and unchecked states? I tried using invalidateOptionsMenu() but that does not do the job, and not only that, with that line in my code I can't get out of the checked state?!?
What happens is that invalidate OptionsMenu() seams to unset the checked state and I end up 'looping', or on every press of that menu item I keep going to the unchecked part of the code where it gets checked and with invalidate it gets unchecked I guess...
Here is the code from my XML file for menu:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/lenslist_menu_add"
android:showAsAction="always"
android:title="#string/add"/>
<item android:id="#+id/lenslist_menu_delete"
android:showAsAction="always"
android:checkable="true"
android:title="#string/delete"/>
</menu>
And here is the java code:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
switch (item.getItemId()) {
case R.id.lenslist_menu_add:
return true;
case R.id.lenslist_menu_delete:
if (item.isChecked() == true) {
item.setChecked(false);
deleteMode = false;
lensAdapter.setDeleteMode(false);
} else {
item.setChecked(true);
deleteMode = true;
lensAdapter.setDeleteMode(true);
}
lensAdapter.notifyDataSetChanged();
return true;
}
return super.onOptionsItemSelected(item);
}
Thanks!
Checkable items appear only in submenus or context menus.
You are using them as main menu items, hence it will not work.
SOURCE: Download the API DEMOS, and open the file ApiDemos/res/menu/checkable.xml, you'll see it as a comment on line 13. I don't know why they don't mention this in the Developer Documentation
reference with comment.:
http://alvinalexander.com/java/jwarehouse/android-examples/platforms/android-2/samples/ApiDemos/res/menu/checkable.xml.shtml
Or just do it yourself
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.item1).setIcon(menu_checked?R.drawable.menu_ico_checked:R.drawable.menu_ico_unchecked);
return super.onPrepareOptionsMenu(menu);
}
and in onOptionsItemSelected do:
....
menu_checked=!menu_checked;
invalidateOptionsMenu();
The best solution is to set the actionLayout of the <Item> to a CheckBox. This solution gives you a native-looking checkbox (with material animations etc), with a font that matches the other items, and it works both as an action and in the submenu.
Create a new layout called action_checkbox.html:
<?xml version="1.0" encoding="utf-8"?>
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:checked="false"
android:textAppearance="#android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Menu"
android:id="#+id/action_item_checkbox"
/>
Set your <Item> like this. Note that you need the Checkable and Checked still in case it is shown in a sub-menu (in which case the actionLayout is ignored.
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
<item android:id="#+id/menu_action_logging"
android:title="#string/action_logging"
android:orderInCategory="100"
android:showAsAction="always"
android:checkable="true"
android:checked="false"
android:actionLayout="#layout/action_checkbox"
/>
</menu>
In your code, when the menu is created we need to a) set the title of the checkbox to match the menu item title, b) restore the checked state of both the menu checkable, and our extra checkbox, and c) add an onClicked() listener for our extra checkbox. In this code I am persisting the state of the checkbox in a RetainedFragment.
// Set the check state of an actionbar item that has its actionLayout set to a layout
// containing a checkbox with the ID action_item_checkbox.
private void setActionBarCheckboxChecked(MenuItem it, boolean checked)
{
if (it == null)
return;
it.setChecked(checked);
// Since it is shown as an action, and not in the sub-menu we have to manually set the icon too.
CheckBox cb = (CheckBox)it.getActionView().findViewById(R.id.action_item_checkbox);
if (cb != null)
cb.setChecked(checked);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
inflater.inflate(R.menu.menu_main, menu);
super.onCreateOptionsMenu(menu, inflater);
// Restore the check state e.g. if the device has been rotated.
final MenuItem logItem = menu.findItem(R.id.menu_action_logging);
setActionBarCheckboxChecked(logItem, mRetainedFragment.getLoggingEnabled());
CheckBox cb = (CheckBox)logItem.getActionView().findViewById(R.id.action_item_checkbox);
if (cb != null)
{
// Set the text to match the item.
cb.setText(logItem.getTitle());
// Add the onClickListener because the CheckBox doesn't automatically trigger onOptionsItemSelected.
cb.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onOptionsItemSelected(logItem);
}
});
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_action_logging:
// Toggle the checkbox.
setActionBarCheckboxChecked(item, !item.isChecked());
// Do whatever you want to do when the checkbox is changed.
mRetainedFragment.setLoggingEnabled(item.isChecked());
return true;
default:
break;
}
return false;
}
Related
I am trying to animate between the visibility mode for a menu.
By default all menu items are hidden but when the user clicks on the edit button i want to show all the items with an animation.
I have achieved the first part of changing the visibility of the menu items and that works fine but the animation part crashes the app.
Here is my code.
When user clicks on edit this is called.By default edit_mode is false.
if (!edit_mode) {
edit_mode = true;
supportInvalidateOptionsMenu();
}
This is the menu code.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_add__custom, menu);
return true;
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem photo = menu.findItem(R.id.photo);
photo.setVisible(edit_mode);
if (edit_mode)
photo.getActionView().animate().alpha(1.0f);
MenuItem date = menu.findItem(R.id.date);
date.setVisible(edit_mode);
if (edit_mode)
date.getActionView().animate().alpha(1.0f);
MenuItem done = menu.findItem(R.id.done);
done.setVisible(edit_mode);
if (edit_mode)
done.getActionView().animate().alpha(1.0f);
return edit_mode;
}
menu.xml
<menu 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">
<item
android:id="#+id/date"
android:icon="#drawable/ic_event_white_24dp"
android:orderInCategory="200"
android:title="Date"
app:showAsAction="ifRoom" />
<item
android:id="#+id/done"
android:icon="#drawable/ic_done_white_24dp"
android:orderInCategory="300"
android:title="Done"
app:showAsAction="ifRoom" />
<item
android:id="#+id/photo"
android:icon="#drawable/ic_photo_white_24dp"
android:orderInCategory="100"
android:title="Done"
app:showAsAction="ifRoom" />
I sure that crash that you are having is there because of NullException thrown by getActionView(). First of all to animate that way you have set the actionView first during onCreateOptionMenu(). That way when you get the actionView in onPrepareOptionsMenu it wont crash because of that and then you can animate it. The onPrepareOptionsMenu executes when you press the menu button so your logic to animate it that time is correct.
If its just the text you want to show in menu item, it should go like this,
final MenuItem photo;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_my_report, menu);
photo = menu.findItem(R.id.action1);
TextView textView = new TextView(this);
textView.setText("I am menu item");
photo.setActionView(textView);
return true;
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
if(someCondition)
{
photo.getActionView().animate().alpha(1.0f);
}
return super.onCreateOptionsMenu(menu);
}
In case you want to have the a complex and customise text you can set it using the layoutInflator service. This could go in your onCreate,
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ImageView view = (ImageView)inflater.inflate(R.layout.some_view, null);
Animation rotation = AnimationUtils.loadAnimation(this, R.anim.fade);
and onCreateOptionMenu,
view.startAnimation(rotation);
photo.setActionView(view);
Its just to get you an idea what needed to be done, you can play around with this and can suit your need.
I am trying to enable the user to stops and starts service which I am implementing from the Menu where the text is will be changed when he clicks it so I want to add ToggleButton as option in the menu tool but nothing is being display in my case now. How can I fix it?
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<ToggleButton
android:id="#+id/toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOff="Off"
android:textOn="On" />
</menu>
MainActivity:
public class MainActivity extends ActionBarActivity {
ToggleButton tButton;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.toggle:
tButton = (ToggleButton) findViewById(R.id.toggle);
tButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (((ToggleButton) v).isChecked()) {
Intent i = new Intent(MainActivity.this,
TrackingService.class);
startService(i);
System.out.println("test is checked, start service");
} else {
// Stop the service when the Menu button clicks.
Intent i = new Intent(MainActivity.this,
TrackingService.class);
stopService(i);
System.out.println("test is NOT checked, stop service");
}
}
});
return true;
default:
return false;
}
}
}
Edit:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.checkable_menu:
if (isChecked = !item.isChecked()) {
item.setChecked(isChecked);
Intent i = new Intent(this, TrackingService.class);
startService(i);
System.out.println("test if onOptionsItemSelected");
} else {
Intent i = new Intent(this, TrackingService.class);
stopService(i);
System.out.println("test else onOptionsItemSelected");
}
return true;
default:
System.out
.println("test default onOptionsItemSelected was invoked.");
return false;
}
}
It is easy. Rather you will have your toggle button on Toolbar.
<item
android:id="#+id/show_secure"
android:enabled="true"
android:title=""
android:visible="true"
app:actionLayout="#layout/show_protected_switch"
app:showAsAction="ifRoom" />
And this is your show_protected_switch.xml layout.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ToggleButton
android:id="#+id/switch_show_protected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/switch_ptotected_btn_selector"
android:textOff=""
android:textOn=""/>
</RelativeLayout>
And in code:
ToggleButton mSwitchShowSecure;
mSwitchShowSecure = (ToggleButton) menu.findItem(R.id.show_secure).getActionView().findViewById(R.id.switch_show_protected);
mSwitchShowSecure.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b){
//Your code when checked
} else {
//Your code when unchecked
}Y
}
});
Output!
It is rather big but you can adjust its size, obviously
I know its very a long time to post an answer, but it may help someone :)
I followed this link and update some of the implemented solution as the app was crashed before these modifications
And below is the full solution:
1- Create a new xml file under layout folder and name it switch_layout.xml and put the below:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<Switch
android:id="#+id/switchAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true" />
</RelativeLayout>
2- Add the below menu item in the main.xml file under menu folder:
<item
android:id="#+id/switchId"
android:title=""
app:actionLayout="#layout/switch_layout"
app:showAsAction="always" />
3- Go to your activity and below is a full implementation for onCreateOptionsMenu method:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
MenuItem item = (MenuItem) menu.findItem(R.id.switchId);
item.setActionView(R.layout.switch_layout);
Switch switchAB = item
.getActionView().findViewById(R.id.switchAB);
switchAB.setChecked(false);
switchAB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
if (isChecked) {
Toast.makeText(getApplication(), "ON", Toast.LENGTH_SHORT)
.show();
} else {
Toast.makeText(getApplication(), "OFF", Toast.LENGTH_SHORT)
.show();
}
}
});
return true;
}
As mentioned above, you can't add toggle button to the menu. You can use the android:checkable property in your menu item to handle the two states.
Something like:
Menu:
<item
android:id="#+id/checkable_menu"
android:checkable="true"
android:title="#string/checkable" />
Activity:
private boolean isChecked = false;
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem checkable = menu.findItem(R.id.checkable_menu);
checkable.setChecked(isChecked);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.checkable_menu:
isChecked = !item.isChecked();
item.setChecked(isChecked);
return true;
default:
return false;
}
}
PS: Copied the code from here.
Or you can just update your item icon on click event to show the two states with item.setIcon(yourDrawable));
You cannot put any widget in <menu> and expect it to work. What you can put there is documented here and it's basically limited to menu <item> and <group>. No buttons, toggles and other widgets are supported. If that would be sufficient you can use android:checkable on the <item> or use old-skool approach and alter menu item depending on the state (if service is on, then your item should read turn service off and vice versa).
In the menu xml you add items not widgets (no buttons/textviews etc)
You simply specify an ID and an ICON for the item, then inflate them in your activities onCreateOptionsMenu() method.
then there is a method called onOptionsItemSelected(MenuItem item) check items id against the ids your expecting.
If its equal to your toggle service option determine service state and alter, if you want to have a toggle button function you can use item.setIcon(drawable) here.
Menu resources are distinct from conventional layouts; you cannot simply add widgets into them and expect them to work. The only elements allowed inside a menu resource is <item> or <group>.
Using a custom layout inside a menu isn't possible, I'm afraid. You may instead want to replace the entire menu with a PopupWindow, and supply your layouts there instead.
You may want to consider two alternatives:
Using a conventional menu entry as a toggle, or
Placing the ToggleButton immediately inside the Actionbar/Toolbar, instead of inside the menu.
States of actionBar
pressed state::
Default state::
What i am doing ::
I am using the splitted actionbar
On click of icon in bottom, I am changing the fragment
Functionality is working fine
What i want::
Is it possible to keep the pressed state of the actionbar item until
i click the next actionbar icon
Then when i say click the star in figure, i want to keep the star in
pressed state as blue color and search icon to return to normal state
Code that i use::
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId()==R.id.searchID){
//SEARCH Button Handling
//((View) item).setBackgroundResource(R.color.actionBarIconPressed);
//item.setIcon(R.color.actionBarIconPressed);
item.setEnabled(true);
ft1=getSupportFragmentManager().beginTransaction();
ft1.hide(fragment1);
//Condition to check whether the fragment is already in container & based on that do appropriate actions
if (getSupportFragmentManager().findFragmentByTag("SearchFragmentTag")==null){
ft1.add(R.id.content_frame, fragSearch, "SearchFragmentTag");
ft1.addToBackStack(null);
ft1.commit();
}else{
ft1.remove(fragSearch);
ft1.add(R.id.content_frame, fragSearch, "SearchFragmentTag");
ft1.commit();
}
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getSupportMenuInflater();
menuInflater.inflate(R.menu.actionbar_sort_menu, menu);
return super.onCreateOptionsMenu(menu);
}
My Questions::
How to modify my code to achieve this feature ?
Is it possible ?
What are the possible ways ?
In onOptionsItemSelected, you can cast the MenuItem into a View and then, change its background. I got a start answer however I think you can improve it and change it according to your needs:
Init your ids in an array:
int[] listItemId = { R.id.searchID, R.id.ratindID, R.id.likeID, R.id.shareID };
Call your options item selected method:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.searchID:
// call private method with int 1
onItemChangeBackground(1);
... // do some stuff
return true;
case R.id.ratindID:
onItemChangeBackground(2);
... // do some stuff
return true;
case R.id.likeID:
onItemChangeBackground(3);
...
return true;
case R.id.shareID:
onItemChangeBackground(4);
...
return true;
}
return super.onOptionsItemSelected(item);
}
Change the background item by switching an int value:
private void onItemChangeBackground(int i) {
// Loop to reinit the background items
for(int ii = 0; ii < listItemId.length; ii++) {
// Cast the MenuItem into a View
( (View) findViewById(listItemId[ii]) )
// And set the default background (for example dark)
.setBackgroundColor(getResources().getColor(R.color.dark));
}
// Selected background
switch(i) {
case 1: ( (View) findViewById(R.id.searchID) )
.setBackgroundColor(getResources().getColor(R.color.blue));
break;
case 2: ( (View) findViewById(R.id.ratindID) )
.setBackgroundColor(getResources().getColor(R.color.blue));
break;
case 3: ( (View) findViewById(R.id.likeID) )
.setBackgroundColor(getResources().getColor(R.color.blue));
break;
case 4: ( (View) findViewById(R.id.shareID) )
.setBackgroundColor(getResources().getColor(R.color.blue));
break;
}
}
You can also set a drawable with a selector by using setBackgroundDrawable() when you want to reinitialize the background items (as you do in the styles.xml). Tested, it works!
Another solution might be to use invalidateOptionsMenu(). This will clear and redraw the menu by recalling onCreateOptionsMenu() again. Keep in mind without call invalidateOptionsMenu() the last method will never be recalled, it's only called at the first rime of the activity's creation. That's why we need to invalidate its content:
Update an integer for each item selected:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId()==R.id.searchID){
nbItemSelected = 1;
invalidateOptionsMenu();
...
} else if(...) {
nbItemSelected = 2;
invalidateOptionsMenu();
} ...
}
Then, change the background while you create the options:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_layout, menu);
if(nItemSelected != 0) {
// Find item, cast the selected item to a view
( (View) menu.findItem(listItemId[nItemSelected]) )
// Change its background
.setBackgroundColor(getResources().getColor(R.color.blue));
}
...
return true;
}
I guess it is possible in this way too, it might work.
Are you creating a TabBarView like for example those you can find in iOs?
If so, an split actionbar is not the best component, it is better to use a ViewPagerIndicator with a ViewPager component. In there you can create your custom icons with your custom icons states and backgrounds.
I have an spare snippet of code I created long time ago for an application that required that component. Maybe you can look it around and take what you need (but I advice you that the code is not the best you can find as I was in a hurry, things can be done better, much better):
https://gist.github.com/aldoborrero/70fbeb062fab0ccc7d87
#Casper did you tried setting custom selector for your ActionBar?
Refer one below
drawable/tab_bg_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Active tab -->
<item android:drawable="#drawable/tab_selected_customtabs" android:state_focused="false" android:state_pressed="false" android:state_selected="true"/>
<!-- Inactive tab -->
<item android:drawable="#drawable/ab_transparent_customtabs" android:state_focused="false" android:state_pressed="false" android:state_selected="false"/>
<!-- Pressed tab -->
<item android:drawable="#drawable/tab_selected_pressed_customtabs" android:state_pressed="true"/>
<!-- Selected tab -->
<item android:drawable="#drawable/tab_selected_focused_customtabs" android:state_focused="true" android:state_pressed="false" android:state_selected="true"/>
</selector>
I have some items in an ActionBar sub menu which I would like to conditionally disable, because they are not applicable for some contexts. I would like them to appear disabled (greyed out) to discourage users from clicking them; but if clicked, I would like to show a toast informing the user of why it is disabled ("can't do A because there is no B", etc). However, if I call MenuItem.setEnabled(false), it seems all the menu item click events do not occur. So, how do I detect click on the disabled menu item?
public class Temp extends Activity implements OnMenuItemClickListener
{
boolean mConditional = true;
protected void onCreate(Bundle state)
{
super.onCreate(state);
}
public boolean onMenuOpened(int featureId, Menu menu)
{
MenuItem item = (MenuItem) menu.findItem(R.id.item2);
if(item != null && mConditional)
{
item.setEnabled(false);
item.setOnMenuItemClickListener(this);
}
return super.onMenuOpened(featureId, menu);
}
#Override
public boolean onMenuItemClick(MenuItem item)
{
//does not fire if item is disabled
Log.e("", item.getTitle().toString());
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
//does not fire if item is disabled
Log.e("", item.getTitle().toString());
return super.onOptionsItemSelected(item);
}
#Override
public boolean onMenuItemSelected(int featureId, MenuItem item)
{
//does not fire if item is disabled
Log.e("", item.getTitle().toString());
return super.onMenuItemSelected(featureId, item);
}
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
}
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/menu"
android:icon="#drawable/ic_launcher"
android:showAsAction="always"
android:title="menu">
<menu>
<item
android:id="#+id/item1"
android:showAsAction="always"
android:title="item 1"/>
<item
android:id="#+id/item2"
android:showAsAction="always"
android:title="item 2"/>
<item
android:id="#+id/item3"
android:showAsAction="always"
android:title="item 3"/>
</menu>
</item>
</menu>
You cannot use setEnabled for this. You need to keep menu items enabled all the time and simulate the "disabled" state by changing the drawable (or modifying it using porterduff filter - have a look at this question on how to dim images in android). Separately, you need to keep a flag indicating the state of the item - tag of the menu item is a good option. Ideally, you'd have something like this:
private void setMenuItemEnable(MenuItem item, boolean enabled) {
int curstate = ((Integer)item.getTag()).intValue();
if(curState == 1 && enabled || curstate == 0 && !enabled) {
return;
}
if(enabled) {
... //update image to remove dimming
item.setTag(1);
}
else {
... //update image by dimming it
item.setTag(0);
}
}
Finally, in your onOptionsItemSelected() method, check the tag of the chosen menu item and either perform the action if the tag is 1 or display the toast if the tag is 0. Visually it will be just what you want, and functionally it'll do what you're after.
I have the following menu layout in my Android app:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/item1"
android:titleCondensed="Options"
android:title="Highlight Options"
android:icon="#android:drawable/ic_menu_preferences" />
<item android:id="#+id/item2"
android:titleCondensed="Persist"
android:title="Persist"
android:icon="#android:drawable/ic_menu_preferences"
android:checkable="true" />
</menu>
My problem is that the second menu item doesn't appear to be "checkable" when I run my app in the Android emulator. There should be a green tick about the item, right? To indicate that its checkable.
Am I doing something wrong?
Layout looks right. But you must check and uncheck menu item in code.
From the documentation:
When a checkable item is selected, the system calls your respective item-selected callback method (such as onOptionsItemSelected()). It is here that you must set the state of the checkbox, because a checkbox or radio button does not change its state automatically. You can query the current state of the item (as it was before the user selected it) with isChecked() and then set the checked state with setChecked().
Wrap the items in a group element, like this:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="all">
<item android:id="#+id/item1"
android:titleCondensed="Options"
android:title="Highlight Options"
android:icon="#android:drawable/ic_menu_preferences">
</item>
<item android:id="#+id/item2"
android:titleCondensed="Persist"
android:title="Persist"
android:icon="#android:drawable/ic_menu_preferences"
android:checkable="true">
</item>
</group>
</menu>
From the Android docs:
The android:checkableBehavior attribute accepts either:
single - Only one item from the group can be checked (radio buttons)
all - All items can be checked (checkboxes)
none - No items are checkable
You can create a checkable menu item by setting the actionViewClass to a checkable widget like android.widget.CheckBox
res/menu/menu_with_checkable_menu_item.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_favorite"
android:checkable="true"
android:title="#string/action_favorite"
app:actionViewClass="android.widget.CheckBox"
app:showAsAction="ifRoom|withText" />
</menu>
And you can can even style it to be a checkable star if you set actionLayout to a layout with a styled android.widget.CheckBox
res/layout
/action_layout_styled_checkbox.xml
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
style="?android:attr/starStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
res/menu/menu_with_checkable_star_menu_item.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_favorites"
android:checkable="true"
android:title="#string/action_favorites"
app:actionLayout="#layout/action_layout_styled_checkbox"
app:showAsAction="ifRoom|withText" />
</menu>
To set the value
menuItem.setChecked(true/false);
To get the value
menuItem.isChecked()
Cast MenuItem to CheckBox
CheckBox checkBox= (CheckBox) menuItem.getActionView();
I've found that the best solution was to just use the onOptionsItemSelected() method as of my current API (27-28).
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
//Copy from here...
int itemId = item.getItemId();
if(item.isChecked())
{
if(R.id.edit_tile_checkbox == itemId) //Individual checkbox logic
{ /*TODO unchecked Action*/}
item.setChecked(false); //Toggles checkbox state.
}
else
{
if(R.id.edit_tile_checkbox == itemId) //Individual checkbox logic
{/*TODO checked Action*/}
item.setChecked(true); //Toggles checkbox state.
}
//...To here in to your onOptionsItemSelected() method, then make sure your variables are all sweet.
return super.onOptionsItemSelected(item);
}
I spent way to long on here for this answer. and for whatever reason, the answers above didn't help (I'm a returning newbie I probably mucked something up I'm sure).
There could be a better way of doing this so helpful criticism is welcomed.
READ THIS
As has been said the "manual checking" is only the tip of the iceberg. It flashes the menu away so fast the users don't see anything happen and it is very counter intuitive, frustrating, and effectively utter crap. The REAL TASK (therefore) is allowing the check box event to be digested by the users mind.
Good news: this can be done and it does work and this is how you do it. #TouchBoarder had it best so I will copy his code. then develop it.
the idea is to detect if the checkbox is clicked, then (and only if that one is picked) slightly suppress the menu removal, add a timer for 500ms then close the menu, this give the "tick" animation of the checkbox time to run and creates the right "feel"
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_favorite"
android:checkable="true"
android:title="#string/action_favorite"
app:actionViewClass="android.widget.CheckBox"
app:showAsAction="ifRoom|withText" />
</menu>
then you make this method as usual, but you make sure you add all this extra bumpf
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the bottom bar and the top bar (weird)
BottomAppBar bottomBar = findViewById(R.id.bottom_app_bar_help);
Menu bottomMenu = bottomBar.getMenu();
getMenuInflater().inflate(R.menu.bottom_nav_menu, bottomMenu);
for (int i = 0; i < bottomMenu.size(); i++) {
bottomMenu.getItem(i).setOnMenuItemClickListener(item -> {
if (item.getItemId()==R.id.action_favorite){
item.setChecked(!item.isChecked());
// Keep the popup menu open
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
item.setActionView(new View(frmMain.this));
item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
#Override
public boolean onMenuItemActionExpand(MenuItem item) {
final Handler handler = new Handler();
handler.postDelayed(() -> bottomMenu.close(), 500);
return false;
}
#Override
public boolean onMenuItemActionCollapse(MenuItem item) {
final Handler handler = new Handler();
handler.postDelayed(() -> bottomMenu.close(), 500);
return false;
}
});
return false;
}
else {
return onOptionsItemSelected(item);
}
});
}
return true;
}
the other menu events are here
public boolean onOptionsItemSelected(MenuItem item) {
// Bottom Bar item click
try {
switch (item.getItemId()) {
case R.id.mnuExit:
MenuClick(ClickType.LOGOUT);
return true;
case R.id.mnuList:
MenuClick(ClickType.LIST);
return true;
default:
return super.onOptionsItemSelected(item);
}
} catch (Exception e) {
e.printStackTrace();
}
return super.onOptionsItemSelected(item);
}
Answering because the answers here seem long and convoluted.. I have some exact Kotlin code here
Override your activity at the top and override the function onMenuItemClick, Have a function to handle the button click to open the menu.
Have an array or list which holds the checked value and sets the check when the menu is re-created
Note: This code does not keep the menu open, It only ensures that checked items remain checked.
I noted there are lots of solutions to that on stack overflow, so have a look at them if that's what you desire
class exampleActivity : AppCompatActivity(), PopupMenu.OnMenuItemClickListener {
private var checkChecked = arrayListOf(false,false)
//some code
fun clickBTN(v: View){
val popup = PopupMenu(this,v)
popup.setOnMenuItemClickListener(this)
popup.inflate(R.menu.yourmenufilename)
//assuming you have 2 or more menu items
popup.menu[0].isChecked = checkChecked[0]
popup.menu[1].isChecked = checkChecked[1]
popup.show()
}
override fun onMenuItemClick(item: MenuItem?): Boolean {
when(item?.itemID){
R.id.item0 -> {
item.isChecked = !item.isChecked
checkChecked[0] = item.isChecked
return true
}
R.id.item1 -> {
item.isChecked = !item.isChecked
checkChecked[1] = item.isChecked
return true
}
}
}
of course in XML you should have your Button and Menu setup. An example menu is here
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/item0"
android:title="#string/hi"
android:checkable="true"/>
<item android:id="#+id/item1"
android:title="#string/yo"
android:checkable="true"/>
</menu>
This may be theme dependant but my menu didn't show a checkbox. I found this :
Note: Menu items in the Icon Menu cannot display a checkbox or radio
button. If you choose to make items in the Icon Menu checkable, then
you must personally indicate the state by swapping the icon and/or
text each time the state changes between on and off.
For Adding Menu items Programmatically,
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add("Item1").setActionView(R.layout.action_layout_checkbox).setCheckable(true);
return super.onCreateOptionsMenu(menu);
}
res/layout /action_layout_checkbox.xml
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
I have two items in the menu and set to checkable in menu.xml file like below
<item
android:id="#+id/A"
android:title="A"
app:showAsAction="never"
android:checkable="true"/>
<item
android:id="#+id/B"
android:title="B"
app:showAsAction="never"
android:checkable="true"/>
and logic for the menu checkboxes is below.
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.A:
//logic goes here
if(item.isChecked())
{
//logic is it is checked
item.setChecked(false);
}
else
{
//logic is it is not checked
item.setChecked(true);
}
return true;
case R.id.B:
//logic for second checkbox goes here
if(item.isChecked())
{
//logic is it is checked
item.setChecked(false);
}
else
{
//logic is it is not checked
item.setChecked(true);
}
return true;
default:
return super.onOptionsItemSelected(item);
}