如何在Android上选择子菜单中的多个复选框?

发布于 2024-11-29 21:19:47 字数 1353 浏览 2 评论 0原文

我有一个带有“添加/删除”选项的选项菜单,单击该选项时会显示一个可检查列表。我当前拥有的代码的问题是您一次只能选择一项,并且菜单消失。我希望能够一次检查列表中的多个项目,并且在用户触摸屏幕上其他位置之前它不会消失。我该怎么做?这是我的总体思路:

<?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>

@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);
   }
}

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);
   }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

掀纱窥君容 2024-12-06 21:19:47

你好,披头士狂人!

老实说,我不知道您正在寻找的内容是否可行(编辑:以您实现它的方式,作为子菜单),但我会这样做:

创建一个看起来像的活动就像你想要显示的子菜单。

它可能看起来有点复杂,但它很简单,通过这种方式,如果你选择/取消选择一个项目,它不会消失,并且你可以实现更多的功能。

以下是我个人的做法:


  • 创建一个类来表示子菜单项。它应该包含一个字符串(描述)和一个布尔值(用于存储是否被检查)。
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;
    }

}

正如您所看到的,该类实现了 Serialized,以便该类的对象可以使用意图/捆绑包从一个活动传递到另一个活动。

  • 将以下内容添加到您当前的活动中(我假设它名为 MainActivity,因此在尝试时,请将 MainActivity 替换为您的活动名称)。
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 );
}

列表(ArrayList)用于托管所有带有复选框的设置子菜单项。
正如您所看到的,每个 SettingCheckBox 对象都有一个描述和一个状态(选中或未选中)。默认情况下,创建后,对象状态为未选中
您应该在 onCreate 方法中初始化列表。

静态和最终变量SETTING_CHECK_BOX用作在活动重新创建之前/之后保存/恢复该列表内容的键(例如屏幕旋转),并将设置列表从一个活动传递到另一个活动。 (稍后解释)

  • 删除子菜单,以便菜单 xml 文件如下所示:
<?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>

不再需要子菜单,因为您将实现一个行为类似的活动。
现在,要将菜单项链接到将显示设置的活动,您应该在当前活动中使用 onOptionsItemSelected 方法,如下所示:

@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 );
    }
}

为结果启动设置活动。这意味着它就像子活动一样,并且可以将结果返回到其父活动。

设置列表通过意图传递到设置活动。

如果子活动结束并向父活动返回数据,则调用以下方法:

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

您应该使子/设置活动返回(新的/修改的)设置列表,并且如上所示,设置新列表。

  • 创建以下名为 sub_menu 的布局 xml 文件:
<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> 

这是将充当子菜单的活动的布局。它实际上是一个列表活动,可以容纳任意数量的选项(您只需将它们添加到上面活动中声明的数组列表中)。

  • 创建名为 sub_menu_item 的以下布局 xml 文件:
<?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>

这是列表中每行的布局,有一个文本视图和复选框(就像您已经使用的子菜单一样)。

  • 创建一个名为 MyActivity_Settings 的新类,该类应包含以下内容:
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

}

该类代表将充当子菜单的活动。正如我之前所说,它是一个列表活动(因此应该扩展ListActivity)。为了显示列表中的各种选项,您需要一个适配器(对于这种情况,数组适配器就足够了),这就是 MyActivity_Settings_Adapter 类(扩展 ArrayAdapter )的作用。

如果列表活动完成(用户单击后退按钮,或显示为对话框的活动外部的任何位置),它(活动)将带有新选中值的新选项列表返回给父活动。

适配器将构建要显示的列表的每一行。
此外,适配器将为每个复选框分配一个单击侦听器,以便如果选中(或取消选中)选项将进行相应修改。

如果您单击子菜单之外的任何位置(或者只需按后退按钮),子菜单就会消失,并且用户选择将保留在主活动的布尔数组中。

如果您不熟悉 ListActivity 和 ArrayAdapter,这个教程将会有很大帮助!

  • 不要忘记将其添加到您的 android 清单 xml 文件中(在应用程序标记中):
    <activity android:name=".MyActivity_Settings"
        android:theme="@android:style/Theme.Dialog" />

应用的主题 (@android:style/Theme.Dialog) 将使活动看起来像一个子菜单。

希望有帮助!
我尝试了一下,效果非常好!尝试一下并让我知道会发生什么。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文