Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to select multiple checkboxes in submenu on Android?

I have an options menu with an "Add/Remove" option that, when clicked, shows a checkable list. The problem with the code that I currently have is that you can only select one item at a time, and the menu disappears. I want to be able to check multiple items in the list at once, and for it not to disappear until the user touches a spot elsewhere on the screen. How can I do this? Here's the general idea of what I have:

<?xml version="1.0" encoding="UTF-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/select_options" 
          android:title="Add/Remove">
        <menu>
            <group android:checkableBehavior="all">
                <item android:id="@+id/A" 
                      android:checked="true" 
                      android:title="Option One" />
                <item android:id="@+id/B" 
                      android:checked="true" 
                      android:title="Option Two" />
            </group>
        </menu>
    </item>
</menu>

and

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.selection_menu, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item){
    switch (item.getItemId()){
    case R.id.A:
        item.setChecked(!item.isChecked());
        return true;
    case R.id.B:
        item.setChecked(!item.isChecked());
        return true;
   default:
        return super.onOptionsItemSelected(item);
   }
}
like image 956
Kalina Avatar asked Aug 16 '11 00:08

Kalina


People also ask

How do I select multiple checkboxes?

Simply check or uncheck multiple checkboxes at a time by clicking and dragging. Allows you to check multiple checkboxes quickly by CLICKING & DRAGGING or even quicker with an ALT+CLICK & DRAG area select.

How do I toggle CheckBox in Android?

By default, the android CheckBox will be in the OFF (Unchecked) state. We can change the default state of CheckBox by using android:checked attribute. In case, if we want to change the state of CheckBox to ON (Checked), then we need to set android:checked = “true” in our XML layout file.


1 Answers

Hello TheBeatlemaniac !

I honestly don't know if what you are seeking for is doable or not ( EDIT : in the way you are implementing it, as a sub menu ), but I would have done it this way:

Create an activity that looks like the sub menu you want to display.

It might seem a little bit more complicated but it is straight forward and in this manner, it will not disappear if you select / deselect an item, and you can implement much more functionality.

Here's how I would have personally done it:


  • Create a class to represent a sub menu item. It should contain a string (description), and a boolean (to store if it is checked or not).
public class SettingCheckBox implements Serializable {

    private static final long serialVersionUID = 1L;

    private static final String DEFAULT_DESCRIPTION = "N/A";

    private final String description;

    private boolean checked;

    public String getDescription () {
        return description == null ? DEFAULT_DESCRIPTION  : description;
    }

    public void setChecked ( final boolean checked ) {
        this.checked = checked;
    }

    public boolean getChecked () {
        return checked;
    }

    public SettingCheckBox ( final String description ) {
        this.description = description;
    }

}

As you can see, the class implements Serializable so that objects of that class can be passed from an activity to another using intents/bundles.

  • Add the following to your current activity (I assumed it is called MainActivity, so while you try it, replace MainActivity by your activity name).
public static final String SETTING_CHECK_BOX = "SETTING_CHECK_BOX";

private ArrayList < SettingCheckBox > settingList;

@Override
public void onCreate(Bundle savedInstanceState) {
    // ... 
    settingList = new ArrayList < SettingCheckBox > ();
    settingList.add ( new SettingCheckBox ( "Option A" ) );
    settingList.add ( new SettingCheckBox ( "Option B" ) );
    // ... add more items
    // restore any previously saved list
    if ( savedInstanceState != null ) {
        settingList = (ArrayList < SettingCheckBox >) savedInstanceState.getSerializable ( SETTING_CHECK_BOX );
    }
    // ...
}

protected void onSaveInstanceState ( Bundle outState ) {
    super.onSaveInstanceState ( outState );
    outState.putSerializable ( SETTING_CHECK_BOX , settingList );
}

The list (ArrayList) is used to host all your setting sub menu items with the check boxes. As you can see, each SettingCheckBox object has a description and a state (checked or unchecked). By default, once create, the object state is unchecked. You should initialize the list inside the onCreate method.

The static and final variable SETTING_CHECK_BOX is used as key to save/restore the content of that list before/after activity recreations (like a screen rotation), and also to pass the settings list from an activity to another. (explained later on)

  • Remove your sub menu, so that the menu xml file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/select_options" 
        android:title="Add/Remove">
    </item>
</menu>

No need for the sub menu anymore since you will be implementing an activity that acts like one. Now, to link the menu item to the activity that will display the settings, you should use the onOptionsItemSelected method inside your current activity like this :

@Override
public boolean onOptionsItemSelected ( MenuItem menuItem ) {
    if ( menuItem.getItemId () == R.id.select_options ) {
        Intent intent = new Intent ( this , MyActivity_Settings.class );
        intent.putExtra ( SETTING_CHECK_BOX , settingList );
        startActivityForResult ( intent , 0 );
    }
}

The settings activity is started for a result. It means like it behaves as a child activity, and can return a result to its parent activity.

The settings list is passed to the settings activity via the intent.

If the child activity ends and returns data to the parent activity, the following method is called :

protected void onActivityResult ( int requestCode , int resultCode , Intent data ) {
    if ( resultCode != RESULT_OK || data == null )
        return;
    settingList = (ArrayList < SettingCheckBox >) data.getSerializableExtra ( SETTING_CHECK_BOX );
}

You should make the child / settings activity return the (new/modified) list of settings, and as demonstrated above, the new list is set.

  • Create the following layout xml file called sub_menu:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

</LinearLayout> 

This is the layout of the activity that will act as your sub menu. It is actually a list activity, and can hold as many options as you want (you just add them in the array list declared in your activity above).

  • Create the following layout xml file called sub_menu_item:
<?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"
    android:gravity="center_vertical" >

    <TextView
        android:id="@+id/option_title"
        android:layout_width="0dip"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textAppearance="@android:style/TextAppearance.Medium" />

    <CheckBox
        android:id="@+id/option_checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

This is the layout of each row in the list, there is a text view and check box ( just like the sub menu you were already using).

  • Create a new class entitled MyActivity_Settings that should contain the following:
public class MyActivity_Settings extends ListActivity {

    private ArrayList < SettingCheckBox > settingList;

    @Override
    public void onCreate ( Bundle savedInstanceState ) {
        super.onCreate(savedInstanceState);
        setContentView ( R.layout.sub_menu );
        setTitle ( "Add/Remove" );
        settingList = getIntent ().getSerializableExtra ( MainActivity.SETTING_CHECK_BOX );
        if ( savedInstanceState != null ) {
            settingList = (ArrayList < SettingCheckBox >) savedInstanceState.getSerializable ( MainActivity.SETTING_CHECK_BOX );
        }
        setListAdapter ( new MyActivity_Settings_Adapter ( this , R.layout.item_layout , settingList ) );
    }

    protected void onSaveInstanceState ( Bundle outState ) {
        super.onSaveInstanceState ( outState );
        outState.putSerializable ( MainActivity.SETTING_CHECK_BOX , settingList );
    }

    @Override
    public void finish () {
        setResult ( RESULT_OK , new Intent ().putExtra ( MainActivity.SETTING_CHECK_BOX , settingList ) );
        super.finish ();
    }

}

class MyActivity_Settings_Adapter extends ArrayAdapter < SettingCheckBox > {

    private final LayoutInflater layoutInflater;
    private final int itemResourceId;

    // Holder pattern (used instead of findViewById for better performance)
    static class ViewHolder {
        public TextView title;
        public CheckBox checkBox;
    }

    // Constructor
    public MyActivity_Settings_Adapter ( ListActivity context, int itemResourceId , List < SettingCheckBox > options ) {
        super ( context , itemResourceId , options );
        layoutInflater = context.getLayoutInflater ();
        this.itemResourceId = itemResourceId;
    }

    // Method called by the list view every time to display a row
    @Override
    public View getView ( int position , View convertView , ViewGroup parent ) {
        // Declare and initialize the row view
        View rowView = convertView;
        // Declare the row view holder
        ViewHolder viewHolder;
        // Check if an inflated view is provided
        if ( rowView == null ) {
            // A new view must be inflated
            rowView = layoutInflater.inflate ( itemResourceId , null );
            // Declare and initialize a view holder
            viewHolder = new ViewHolder ();
            // Retrieve a reference to the row title
            viewHolder.title = (TextView) rowView.findViewById ( R.id.option_title );
            // Retrieve a reference to the row check box
            viewHolder.checkBox = (CheckBox) rowView.findViewById ( R.id.option_checkbox );
            // Store the view holder as tag
            rowView.setTag ( viewHolder );
        } // End if
        else
        // An inflated view is already provided, retrieve the stored view holder
        viewHolder = (ViewHolder) rowView.getTag ();

        // Set the option title
        viewHolder.title.setText ( getItem ( position ).getDescription () );
        // Set the option check box state
        viewHolder.checkBox.setChecked ( getItem ( position ).getChecked () );
        // Assign a click listener to the checkbox
        viewHolder.checkBox.setOnClickListener( new OnClickListener() {

            public void onClick ( View checkBox ) {
                // Retrieve the stored view holder
                ViewHolder viewHolder = (ViewHolder) ((View) checkBox.getParent()).getTag();
                // Update the option state
                getItem ( position ).setChecked ( ! getItem ( position ).getChecked () );
                // Display the new option state
                viewHolder.checkBox.setChecked ( getItem ( position ).getChecked () );
            }
        });

        // Return the row view for display
        return rowView;
    } // End of getView

}

This class represents the activity that will act as your sub menu. As I previously said, it is a List Activity (and hence should extend ListActivity). In order to display the various options inside the list, you need an adapter (array adapter is sufficient for this case), that's the role of the MyActivity_Settings_Adapter class (that extends ArrayAdapter ).

If the list activity finishes (the user clicks on the back button, or anywhere outside the activity which is displayed as a dialog), it (the activity) returns to the parent activity the new list of options with the new checked values.

The adapter will build each row for the list to display. In addition, the adapter will assign a click listener for every check box, so that if checked (or unchecked) the option will be modified accordingly.

And if you click anywhere outside the sub menu (or simply press on the back button), the sub menu will disappear, and the user selections are preserved in the boolean array in your main activity.

If you are not familiar with ListActivity and ArrayAdapter, this tutorial would help a lot !

  • Do not forget to add this in your android manifest xml file (in the application tag):
    <activity android:name=".MyActivity_Settings"
        android:theme="@android:style/Theme.Dialog" />

The theme applied (@android:style/Theme.Dialog) will make the activity look like a sub menu.

Hope it helps ! I tried it and it works perfectly ! Try it and let me know what happens.

like image 146
Leeeeeeelo Avatar answered Nov 15 '22 00:11

Leeeeeeelo