如何将一个3x3的单选框网格分组?

45

正如标题所述,我正在尝试将一个3x3单选框网格分组为一个单一的单选组。在之前的问题中,我了解到如果单选按钮要对应于一个单一的组,它们必须是对应单选组的直接子元素。当我试图将整个表布局(其中包含单选按钮的表格行)封装在单选组中时,我通过困难的尝试学到了这一点。

遇到这个障碍后,我尝试了以下方法:

<TableLayout android:id="@+id/table_radButtons" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content"
        android:layout_below="@+id/title_radGroup_buffer">

        <TableRow>
            <RadioGroup android:layout_width="fill_parent" 
                android:layout_height="wrap_content" 
                android:orientation="horizontal"
                android:id="@+id/radGroup1">  

                <RadioButton android:id="@+id/rad1" 
                    android:text="Button1" 
                    android:layout_width="105px" 
                    android:layout_height="wrap_content" 
                    android:textSize="13px"></RadioButton>
                <RadioButton android:id="@+id/rad2" 
                    android:text="Button2" 
                    android:layout_width="105px" 
                    android:textSize="13px" 
                    android:layout_height="wrap_content"></RadioButton>
                <RadioButton android:id="@+id/rad3" 
                    android:text="Button3" 
                    android:layout_width="105px" 
                    android:textSize="13px" 
                    android:layout_height="wrap_content"></RadioButton>
            </RadioGroup>
        </TableRow>
        <TableRow>
            <RadioGroup android:layout_width="fill_parent" 
                android:layout_height="wrap_content" 
                android:orientation="horizontal"
                android:id="@+id/radGroup1">
                  <!-- snippet -->
        </TableRow>
        <!-- snippet --->
</TableLayout>

显然我没有学到教训,因为我再次遇到了障碍。我希望不同表行中的单选按钮能够注意到它们属于同一单选组(为每个组分配相同的ID),但这并没有起作用。

是否有办法将所有这些按钮分组为一个单选组,同时保持我的3x3结构(每行3个单选按钮)?

10个回答

72

如果你像这个例子一样对TableLayout进行子类化,实际上并不难。

/**
 * 
 */
package com.codtech.android.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.RadioButton;
import android.widget.TableLayout;
import android.widget.TableRow;

/**
 * @author diego
 *
 */
public class ToggleButtonGroupTableLayout extends TableLayout  implements OnClickListener {

    private static final String TAG = "ToggleButtonGroupTableLayout";
    private RadioButton activeRadioButton;

    /** 
     * @param context
     */
    public ToggleButtonGroupTableLayout(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }

    /**
     * @param context
     * @param attrs
     */
    public ToggleButtonGroupTableLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void onClick(View v) {
        final RadioButton rb = (RadioButton) v;
        if ( activeRadioButton != null ) {
            activeRadioButton.setChecked(false);
        }
        rb.setChecked(true);
        activeRadioButton = rb;
    }

    /* (non-Javadoc)
     * @see android.widget.TableLayout#addView(android.view.View, int, android.view.ViewGroup.LayoutParams)
     */
    @Override
    public void addView(View child, int index,
            android.view.ViewGroup.LayoutParams params) {
        super.addView(child, index, params);
        setChildrenOnClickListener((TableRow)child);
    }


    /* (non-Javadoc)
     * @see android.widget.TableLayout#addView(android.view.View, android.view.ViewGroup.LayoutParams)
     */
    @Override
    public void addView(View child, android.view.ViewGroup.LayoutParams params) {
        super.addView(child, params);
        setChildrenOnClickListener((TableRow)child);
    }


    private void setChildrenOnClickListener(TableRow tr) {
        final int c = tr.getChildCount();
        for (int i=0; i < c; i++) {
            final View v = tr.getChildAt(i);
            if ( v instanceof RadioButton ) {
                v.setOnClickListener(this);
            }
        }
    }

    public int getCheckedRadioButtonId() {
        if ( activeRadioButton != null ) {
            return activeRadioButton.getId();
        }

        return -1;
    }
}

并创建这样的布局(当然,你需要清理它,但你已经有了想法)

<?xml version="1.0" encoding="utf-8"?>
<com.codtech.android.view.ToggleButtonGroupTableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content" android:layout_height="wrap_content"
    android:id="@+id/radGroup1">
    <TableRow>
            <RadioButton android:id="@+id/rad1" android:text="Button1"
                android:layout_width="105px" android:layout_height="wrap_content"
                android:textSize="13px" />
            <RadioButton android:id="@+id/rad2" android:text="Button2"
                android:layout_width="105px" android:textSize="13px"
                android:layout_height="wrap_content" />
            <RadioButton android:id="@+id/rad3" android:text="Button3"
                android:layout_width="105px" android:textSize="13px"
                android:layout_height="wrap_content" />
    </TableRow>
    <TableRow>
            <RadioButton android:id="@+id/rad1" android:text="Button1"
                android:layout_width="105px" android:layout_height="wrap_content"
                android:textSize="13px" />
            <RadioButton android:id="@+id/rad2" android:text="Button2"
                android:layout_width="105px" android:textSize="13px"
                android:layout_height="wrap_content" />
            <RadioButton android:id="@+id/rad3" android:text="Button3"
                android:layout_width="105px" android:textSize="13px"
                android:layout_height="wrap_content" />
    </TableRow>
    <TableRow>
            <RadioButton android:id="@+id/rad1" android:text="Button1"
                android:layout_width="105px" android:layout_height="wrap_content"
                android:textSize="13px" />
            <RadioButton android:id="@+id/rad2" android:text="Button2"
                android:layout_width="105px" android:textSize="13px"
                android:layout_height="wrap_content" />
            <RadioButton android:id="@+id/rad3" android:text="Button3"
                android:layout_width="105px" android:textSize="13px"
                android:layout_height="wrap_content" />
    </TableRow>
</com.codtech.android.view.ToggleButtonGroupTableLayout>

2
我需要仔细查看一下,看看我是否能够准确理解你正在做什么。谢谢。 - Fizz
1
每当选中的按钮发生变化时,如何获取其ID? - wolverine
1
@dtmilano:在我的情况下,我需要默认设置第一个单选按钮。然后你的代码就不起作用了。但是我注意到,如果我点击第一个单选按钮,它就开始工作了。我需要做哪些改变才能让代码在我的情况下工作? - Monica
@muetzenflo:看起来你非常理解代码,所以我想在我的情况下向你寻求帮助(请参见我的第一条评论)。 - Monica
3
我解决了我的问题!如果在布局文件中最初有一个单选按钮被选中,那么在setChildrenOnClickListener方法中需要将activeRadioButton设置为这个被选中的单选按钮。 - Monica
显示剩余7条评论

11

在上述https://dev59.com/5XE95IYBdhLWcg3wRLwt#2383978的答案之后,我得到了另一种解决此问题的方法,我添加了其他功能,例如保存组状态以及清除类似于单选按钮组的检查功能。

import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.IdRes;
import android.util.AttributeSet;
import android.view.View;
import android.widget.RadioButton;
import android.widget.TableLayout;
import android.widget.TableRow;

public class RadioGridGroup extends TableLayout implements View.OnClickListener {

    private static final String TAG = "ToggleButtonGroupTableLayout";
    private int checkedButtonID = -1;

    /**
     * @param context
     */
    public RadioGridGroup(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }

    /**
     * @param context
     * @param attrs
     */
    public RadioGridGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void onClick(View v) {
        if (v instanceof RadioButton) {
            int id = v.getId();
            check(id);
        }
    }

    private void setCheckedStateForView(int viewId, boolean checked) {
        View checkedView = findViewById(viewId);
        if (checkedView != null && checkedView instanceof RadioButton) {
            ((RadioButton) checkedView).setChecked(checked);
        }
    }

    /* (non-Javadoc)
     * @see android.widget.TableLayout#addView(android.view.View, int, android.view.ViewGroup.LayoutParams)
     */
    @Override
    public void addView(View child, int index,
                        android.view.ViewGroup.LayoutParams params) {
        super.addView(child, index, params);
        setChildrenOnClickListener((TableRow) child);
    }


    /* (non-Javadoc)
     * @see android.widget.TableLayout#addView(android.view.View, android.view.ViewGroup.LayoutParams)
     */
    @Override
    public void addView(View child, android.view.ViewGroup.LayoutParams params) {
        super.addView(child, params);
        setChildrenOnClickListener((TableRow) child);
    }


    private void setChildrenOnClickListener(TableRow tr) {
        final int c = tr.getChildCount();
        for (int i = 0; i < c; i++) {
            final View v = tr.getChildAt(i);
            if (v instanceof RadioButton) {
                v.setOnClickListener(this);
            }
        }
    }


    /**
     * @return the checked button Id
     */
    public int getCheckedRadioButtonId() {
        return checkedButtonID;
    }


    /**
     * Check the id
     *
     * @param id
     */
    public void check(@IdRes int id) {
        // don't even bother
        if (id != -1 && (id == checkedButtonID)) {
            return;
        }
        if (checkedButtonID != -1) {
            setCheckedStateForView(checkedButtonID, false);
        }
        if (id != -1) {
            setCheckedStateForView(id, true);
        }
        setCheckedId(id);
    }

    /**
     * set the checked button Id
     *
     * @param id
     */
    private void setCheckedId(int id) {
        this.checkedButtonID = id;
    }

    public void clearCheck() {
        check(-1);
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }

        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());

        this.checkedButtonID = ss.buttonId;
        setCheckedStateForView(checkedButtonID, true);
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState savedState = new SavedState(superState);
        savedState.buttonId = checkedButtonID;
        return savedState;
    }

    static class SavedState extends BaseSavedState {
        int buttonId;

        /**
         * Constructor used when reading from a parcel. Reads the state of the superclass.
         *
         * @param source
         */
        public SavedState(Parcel source) {
            super(source);
            buttonId = source.readInt();
        }

        /**
         * Constructor called by derived classes when creating their SavedState objects
         *
         * @param superState The state of the superclass of this view
         */
        public SavedState(Parcelable superState) {
            super(superState);
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(buttonId);
        }

        public static final Parcelable.Creator<SavedState> CREATOR =
                new Parcelable.Creator<SavedState>() {
                    public SavedState createFromParcel(Parcel in) {
                        return new SavedState(in);
                    }

                    public SavedState[] newArray(int size) {
                        return new SavedState[size];
                    }
                };
    }
}

并在XML中按以下方式使用:
<com.test.customviews.RadioGridGroup xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <TableRow android:layout_marginTop="@dimen/preview_five">

        <RadioButton
            android:id="@+id/rad1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button1" />

        <RadioButton
            android:id="@+id/rad2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button2" />
    </TableRow>

    <TableRow android:layout_marginTop="@dimen/preview_five">

        <RadioButton
            android:id="@+id/rad3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button3" />

        <RadioButton
            android:id="@+id/rad4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button4" />
    </TableRow>

    <TableRow android:layout_marginTop="@dimen/preview_five">

        <RadioButton
            android:id="@+id/rad5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button5" />

        <RadioButton
            android:id="@+id/rad6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button6" />
    </TableRow>

    <TableRow android:layout_marginTop="@dimen/preview_five">

        <RadioButton
            android:id="@+id/rad7"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button7" />

        <RadioButton
            android:id="@+id/rad8"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button8" />
    </TableRow>
</com.test.customviews.RadioGridGroup>

如果有其他的改进意见,请在评论区留言。


在任何Java包中,您都可以放置它。 - Nigam Patro
1
工作得很好!但XML文件中有一个小错别字:wrap_contact应更正为wrap_content。 - KYHSGeekCode
我正在尝试在运行时添加TableRow和RadioButton,它可以工作,但也允许多选,有什么解决方案吗? - PriyankVadariya
@PriyankVadariya 我们可以做到。但是,我们需要修改解决方案。 - Nigam Patro
这在 Android 10 上运作良好。而 Rajath 的答案在我的 Poco X3 NFC 上无法在 Android 10 上工作。 - Rohaitas Tanoli

7
这使用了自定义GridLayout并带有RadioGroup功能。感谢Saikrishnan Ranganathan对saiaspire/RadioGridGroup的贡献
<com.sample.RadioGridGroup
        xmlns:grid="http://schemas.android.com/apk/res-auto"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        grid:columnCount="3"
        grid:useDefaultMargins="true">

        <android.support.v7.widget.AppCompatRadioButton
            android:checked="true"
            android:text="Text1"
            grid:layout_columnWeight="1"/>

        <android.support.v7.widget.AppCompatRadioButton
            android:text="Text2"
            grid:layout_columnWeight="1"/>

        <android.support.v7.widget.AppCompatRadioButton
            android:text="Text3"
            grid:layout_columnWeight="1"/>

        <android.support.v7.widget.AppCompatRadioButton
            android:text="Text4"
            grid:layout_columnWeight="1"/>

        <android.support.v7.widget.AppCompatRadioButton
            android:text="Text5"
            grid:layout_columnWeight="1"/>

        <android.support.v7.widget.AppCompatRadioButton
            android:text="Text6"
            grid:layout_columnWeight="1"/>

        <android.support.v7.widget.AppCompatRadioButton
            android:text="Text7"
            grid:layout_columnWeight="1"/>

        <android.support.v7.widget.AppCompatRadioButton
            android:text="Text8"
            grid:layout_columnWeight="1"/>

        <android.support.v7.widget.AppCompatRadioButton
            android:text="Text9"
            grid:layout_columnWeight="1"/>

    </com.sample.RadioGridGroup>

1
谢谢,我在组内使用了LinearLayout。虽然它显示正常,但功能不正常。你救了我啊。 - other Tall guy
这个在Android Studio中渲染得很好,但在我的POCO X3 NFC安卓10上无法运行。上面的带有表格布局的答案可以运行。 - Rohaitas Tanoli

6

我个人会选择嵌套的RadioGroups。根RadioGroup将具有垂直方向,其子元素将是具有水平方向的三个RadioGroups。

<RadioGroup
    android:orientation="vertical">
    <RadioGroup
        android:id="@+id/rg_1"
        android:orientation="horizontal">
        <RadioButton />
        <RadioButton />
        <RadioButton />
    </RadioGroup>
    <RadioGroup
        android:id="@id/rg_2"
        android:orientation="horizontal">
        <RadioButton />
        <RadioButton />
        <RadioButton />
    </RadioGroup>
    <RadioGroup
        android:id="@+id/rg_3"
        android:orientation="horizontal">
        <RadioButton />
        <RadioButton />
        <RadioButton />
    </RadioGroup>
</RadioGroup>

每个子RadioGroup都将有一个ID,Java验证方法中的RadioGroup对象将调用该ID。如下所示:

RadioGroup rg_1 = (RadioGroup) findViewById(R.id.rg_1);
RadioGroup rg_2 = (RadioGroup) findViewById(R.id.rg_2);
RadioGroup rg_3 = (RadioGroup) findViewById(R.id.rg_3);

现在只需在 switch case 中使用 clearCheck() 即可清除其他两个 RadioGroup 的选中状态。代码如下:
case R.id.radioButton_1:
            if (checked) {
                rg_2.clearCheck();
                rg_3.clearCheck();
            }
            break;

我发现这比其他答案简单... ;D - kevz

6

我编写了一个非常简单的GridLayout子类,像一个RadioGroup一样。其外观如下图所示:

GridLayout RadioGroup with RadioButton

要使用它,首先添加GridLayout库:

implementation "androidx.gridlayout:gridlayout:1.0.0-beta01"

然后复制粘贴 GridLayout 子类:

/**
 * A GridLayout subclass that acts like a RadioGroup. Important: it only accepts RadioButton as children.
 * Inspired by https://dev59.com/5XE95IYBdhLWcg3wRLwt#2383978
 */
class GridRadioGroup @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : GridLayout(context, attrs, defStyleAttr), View.OnClickListener {

    @IdRes var selectedRadioButtonId: Int? = null
    get() = getSelectedRadioButton()?.id
    private set

    private fun getSelectedRadioButton(): RadioButton? {
        for (index in 0 until childCount) {
            val radioButton = getChildAt(index) as RadioButton
            if (radioButton.isChecked) return radioButton
        }
        return null
    }

    override fun onClick(view: View) {
        // While this looks inefficient, it does fix a bug (2 RadioButtons could be selected at the
        // same time) when navigating back by popping-up a fragment from the backstack.
        for (index in 0 until childCount) {
            val radioButton = getChildAt(index) as RadioButton
            radioButton.isChecked = false
        }
        val radioButton = view as RadioButton
        radioButton.isChecked = true
    }

    override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
        super.addView(child, index, params)
        child?.setOnClickListener(this)
    }

}

最后,只需使用它:

<com.example.ui.GridRadioGroup
    android:id="@+id/gridRadioGroup"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:columnCount="2"
    >

    <RadioButton
        android:id="@+id/option_1"
        style="@style/GridRadioButton"
        android:drawableTop="@drawable/option_1"
        android:text="@string/register_choose_goal_option_1"
        app:layout_columnWeight="1"
        app:layout_rowWeight="1"
        />

    <RadioButton
        android:id="@+id/option_2"
        style="@style/GridRadioButton"
        android:drawableTop="@drawable/option_2"
        android:text="@string/option_2"
        app:layout_columnWeight="1"
        app:layout_rowWeight="1"
        tools:checked="true"
        />

    <RadioButton
        android:id="@+id/option_3"
        style="@style/GridRadioButton"
        android:drawableTop="@drawable/option_3"
        android:text="@string/option_3"
        app:layout_columnWeight="1"
        app:layout_rowWeight="1"
        tools:checked="true"
        />

    <RadioButton
        android:id="@+id/option_4"
        style="@style/GridRadioButton"
        android:drawableTop="@drawable/preparar_carrera"
        android:text="@string/option_4"
        app:layout_columnWeight="1"
        app:layout_rowWeight="1"
        />

</com.example.ui.GridRadioGroup>

样式:
<style name="GridRadioButton">
    <item name="android:layout_width">0dp</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:layout_margin">10dp</item>
    <item name="android:background">@drawable/button_option_background_selector</item>
    <item name="android:drawablePadding">16dp</item>
    <item name="android:button">@null</item>
    <item name="android:gravity">center_horizontal</item>
    <item name="android:padding">20dp</item>
    <item name="android:textColor">@drawable/text_button_option_color_selector</item>
    <item name="android:textSize">14sp</item>
</style>

这段代码很简单,但对我来说运行良好。如果你想要更加完整的内容(例如一个OnCheckedChangeListener),那么可以查看这个替代实现,它显然更加详细,并且在功能上与RadioGroup相当。


3
你唯一的选择是获取 RadioGroup 的源代码,并尝试在 TableLayout 或其他组件中复制其功能。否则,没有办法创建一个 3x3 的 RadioButtons 网格。幸运的是,RadioButton 类并不知道有 RadioGroup 的存在 -- 所有互斥逻辑都在 RadioGroup 中。因此,应该可以创建一个 RadioGrid 或类似的东西,但需要大量工作。

那么也没有办法使用类似 GridView 的东西吗? - Fizz
只有在你对其进行子类化并赋予RadioGroup功能时才能实现。 - CommonsWare

2

RadioGroup 的扩展方式如下所示...

java.lang.Object
 ↳  android.view.View
   ↳    android.view.ViewGroup
       ↳    android.widget.LinearLayout
           ↳    android.widget.RadioGroup

如果您需要将单选按钮排列在网格布局中,可能需要构建自己的自定义代码,该代码扩展自GridLayout


0

这是解决方案

public class RadioGridGroup extends TableLayout implements View.OnClickListener {

private static final String TAG = "ToggleButtonGroupTableLayout";
private int checkedButtonID = -1;
private RadioGridGroup.OnCheckedChangeListener mOnCheckedChangeListener;
/**
 * @param context
 */
public RadioGridGroup(Context context) {
    super(context);
    // TODO Auto-generated constructor stub
}

/**
 * @param context
 * @param attrs
 */
public RadioGridGroup(Context context, AttributeSet attrs) {
    super(context, attrs);
    // TODO Auto-generated constructor stub
}

@Override
public void onClick(View v) {
    if (v instanceof RadioButton) {
        int id = v.getId();
        check(id);
    }
}

private void setCheckedStateForView(int viewId, boolean checked) {
    View checkedView = findViewById(viewId);
    if (checkedView != null && checkedView instanceof RadioButton) {
        ((RadioButton) checkedView).setChecked(checked);
    }
}

/* (non-Javadoc)
 * @see android.widget.TableLayout#addView(android.view.View, int, android.view.ViewGroup.LayoutParams)
 */
@Override
public void addView(View child, int index,
                    android.view.ViewGroup.LayoutParams params) {
    super.addView(child, index, params);
    setChildrenOnClickListener((TableRow) child);
}


/* (non-Javadoc)
 * @see android.widget.TableLayout#addView(android.view.View, android.view.ViewGroup.LayoutParams)
 */
@Override
public void addView(View child, android.view.ViewGroup.LayoutParams params) {
    super.addView(child, params);
    setChildrenOnClickListener((TableRow) child);
}


private void setChildrenOnClickListener(TableRow tr) {
    final int c = tr.getChildCount();
    for (int i = 0; i < c; i++) {
        final View v = tr.getChildAt(i);
        if (v instanceof RadioButton) {
            v.setOnClickListener(this);
        }
    }
}


/**
 * @return the checked button Id
 */
public int getCheckedRadioButtonId() {
    return checkedButtonID;
}


/**
 * Check the id
 *
 * @param id
 */
public void check(@IdRes int id) {
    // don't even bother
    if (id != -1 && (id == checkedButtonID)) {
        return;
    }
    if (checkedButtonID != -1) {
        setCheckedStateForView(checkedButtonID, false);
    }
    if (id != -1) {
        setCheckedStateForView(id, true);
    }
    setCheckedId(id);
}

/**
 * set the checked button Id
 *
 * @param id
 */
private void setCheckedId(int id) {
    if (mOnCheckedChangeListener != null) {
        mOnCheckedChangeListener.onCheckedChanged(this, id);
    }
    this.checkedButtonID = id;
}

public void clearCheck() {
    check(-1);
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    if (!(state instanceof SavedState)) {
        super.onRestoreInstanceState(state);
        return;
    }

    SavedState ss = (SavedState) state;
    super.onRestoreInstanceState(ss.getSuperState());

    this.checkedButtonID = ss.buttonId;
    setCheckedStateForView(checkedButtonID, true);
}

@Override
protected Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();
    SavedState savedState = new SavedState(superState);
    savedState.buttonId = checkedButtonID;
    return savedState;
}

static class SavedState extends BaseSavedState {
    int buttonId;

    /**
     * Constructor used when reading from a parcel. Reads the state of the superclass.
     *
     * @param source
     */
    public SavedState(Parcel source) {
        super(source);
        buttonId = source.readInt();
    }

    /**
     * Constructor called by derived classes when creating their SavedState objects
     *
     * @param superState The state of the superclass of this view
     */
    public SavedState(Parcelable superState) {
        super(superState);
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        out.writeInt(buttonId);
    }

    public static final Parcelable.Creator<SavedState> CREATOR =
            new Parcelable.Creator<SavedState>() {
                public SavedState createFromParcel(Parcel in) {
                    return new SavedState(in);
                }

                public SavedState[] newArray(int size) {
                    return new SavedState[size];
                }
            };
}
public void setOnCheckedChangeListener(RadioGridGroup.OnCheckedChangeListener listener) {
    mOnCheckedChangeListener = listener;
}
public interface OnCheckedChangeListener {

    public void onCheckedChanged(RadioGridGroup group, @IdRes int checkedId);
}}

点击监听器

    RadioGridGroup radioGroup = findViewById(R.id.radioGroup);
   
    radioGroup.setOnCheckedChangeListener(new RadioGridGroup.OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(RadioGridGroup group, int checkedId) {
            
            if (checkedId == R.id.radio6) {
                
            }
        }

    });

XML

<com.customviews.RadioGridGroup 
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

<TableRow android:layout_marginTop="@dimen/preview_five">

    <RadioButton
        android:id="@+id/rad1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1" />

    <RadioButton
        android:id="@+id/rad2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2" />
</TableRow>

<TableRow android:layout_marginTop="@dimen/preview_five">

    <RadioButton
        android:id="@+id/rad3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button3" />

    <RadioButton
        android:id="@+id/rad4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button4" />
</TableRow>

<TableRow android:layout_marginTop="@dimen/preview_five">

    <RadioButton
        android:id="@+id/rad5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button5" />

    <RadioButton
        android:id="@+id/rad6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button6" />
</TableRow>

<TableRow android:layout_marginTop="@dimen/preview_five">

    <RadioButton
        android:id="@+id/rad7"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button7" />

    <RadioButton
        android:id="@+id/rad8"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button8" />
</TableRow>
</com.customviews.RadioGridGroup>

注意:确保所有单选按钮都在TableRow下面。


0

这是一个笨拙的方法,但为什么不呢?为什么不只使用九个RadioGroup或三个水平的每个有三个按钮的RadioGroup。然后可以将每个单选按钮组与 gridView、relativeLayout 等对齐。

然后,不要使用 RadioButton 的标准 OnCheckedChangeListener,而是使用属于 CompoundButton 的同名监听器。

这样一来,每当单选按钮被按下时,您可以使用单选按钮组取消选择单选按钮,然后以编程方式选择被点击的单选按钮。

这是一个可怕的解决方案,但它能像你所希望的那样工作。

以下是我用于在 2x2 按钮布局中执行此操作的示例代码。

  public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        updateList(Hurricane.ELEVATION_ALL);

        watermarkAdapter = new WatermarkAdapter(getActivity(), R.layout.watermark_item, 
                           relatedHurricanes);

        setListAdapter(watermarkAdapter);

        this.getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
        this.getView().setBackgroundResource(R.drawable.splash_screen1);


        getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);


        View v = this.getView();
        filterGroup1 = (RadioGroup)v.findViewById(R.id.filter_rg1);
        filterGroup2 = (RadioGroup)v.findViewById(R.id.filter_rg2);
        filterGroup3 = (RadioGroup)v.findViewById(R.id.filter_rg3);
        filterGroup4 = (RadioGroup)v.findViewById(R.id.filter_rg4);


        rb1 = (RadioButton) v.findViewById(R.id.first_radio_button);//All
        rb2 = (RadioButton) v.findViewById(R.id.second_radio_button);//0-6
        rb4 = (RadioButton) v.findViewById(R.id.third_radio_button);//6-12
        rb3 = (RadioButton) v.findViewById(R.id.fourth_radio_button);//>=5

        rb1.setOnCheckedChangeListener(this);
        rb2.setOnCheckedChangeListener(this);
        rb3.setOnCheckedChangeListener(this);
        rb4.setOnCheckedChangeListener(this);


    }






 @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

        filterGroup1.clearCheck();
        filterGroup2.clearCheck();
        filterGroup3.clearCheck();
        filterGroup4.clearCheck();


        SharedPreferences prefs = PreferenceManager
                .getDefaultSharedPreferences(this.getActivity());

       int cid = buttonView.getId();

       Editor editor     = prefs.edit();
       int elevation = Hurricane.ELEVATION_ALL;
        //All
       if(rb1.getId() == cid){
          elevation = Hurricane.ELEVATION_ALL;
       }  
       //0-6
       else if(rb2.getId() == cid){
           elevation = Hurricane.ELEVATION_6to12;
       }
       //6-12
       else if(rb3.getId() == cid){       
           elevation = Hurricane.ELEVATION_0to6;
       }
       //>=12
       else if(rb4.getId() == cid){
           elevation = Hurricane.ELEVATION_GT12;
       }

       update(StormFragment.NORMAL, elevation);

    }

0

在我看来,更简单的方法是使用 RecyclerView
ViewHolder 只包含一个 RadioButton
data class 包含一个用于显示的 string 属性,以及一个额外的 boolean 属性来切换 isChecked 状态。
ViewHolder 中,我们将在 onClickListener 中切换状态。
RecyclerViewAdapter 将包含一个额外的函数来获取所选项目。

代码示例

BloodType.kt

data class BloodType(val name: String, var isChecked: Boolean)

item_blood_type.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <RadioButton
        android:id="@+id/rb_item_blood_type"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:text="RadioButton"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

BloodTypeAdapter.kt

class BloodTypeAdapter(val data: List<BloodType>) :
    RecyclerView.Adapter<BloodTypeAdapter.BloodTypeHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BloodTypeHolder {
        val view =
            LayoutInflater.from(parent.context).inflate(R.layout.item_blood_type, parent, false)
        return BloodTypeHolder(view)
    }

    override fun onBindViewHolder(holder: BloodTypeHolder, position: Int) {
        holder.itemView.rb_item_blood_type.isChecked = data[position].isChecked
        holder.itemView.rb_item_blood_type.text = data[position].name
    }

    override fun getItemCount(): Int {
        return data.size
    }

    /**
     * @return The selected item from the list.
     */
    fun getCheckedItem(): BloodType {
        data.forEach {
            if (it.isChecked) {
                return it
            }
        }
        return data[0]
    }

    inner class BloodTypeHolder(view: View) : RecyclerView.ViewHolder(view) {
        init {
            itemView.rb_item_blood_type.setOnClickListener {
                data.forEachIndexed { index, bloodType ->
                    bloodType.isChecked = index == adapterPosition
                }
                notifyDataSetChanged()
            }
        }
    }
}

Presenter.kt

//... 
private fun initializeBloodTypeRecyclerView() {
        rvBloodType.layoutManager = GridLayoutManager(context, 2)
        val bloodTypes = mutableListOf<BloodType>()
        bloodTypes.apply {
            add(BloodType("A+", true))
            add(BloodType("A-", false))
            add(BloodType("B+", false))
            //...
        }
        val bloodTypeAdapter = BloodTypeAdapter(bloodTypes)
        rvBloodType.adapter = bloodTypeAdapter
    }
//...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接