簡體   English   中英

RadioGroup 有兩列有十個 RadioButtons

[英]RadioGroup with two columns which have ten RadioButtons

我有一個RadioGroup ,我想在兩列五行中將按鈕彼此相鄰對齊,但我無法實現。 我嘗試過的事情:

  1. RelativeLayout布局 -> 外部RadioGroup -> 內部RadioGroup 所有RadioButtons都被選中,但我只想選擇一個。
  2. RadioGroup : 方向
  3. 跨度,拉伸柱
  4. TableRow
  5. TableLayout

請讓我知道如何創建一個RadioGroup並在其中包含兩列和許多RadioButtons

您可以模擬該RadioGroup以使其看起來只有一個。 例如,您有rg1rg2 (方向設置為vertical (兩列)的RadioGroups )。 要設置這些RadioGroups

rg1 = (RadioGroup) findViewById(R.id.radioGroup1);
rg2 = (RadioGroup) findViewById(R.id.radioGroup2);
rg1.clearCheck(); // this is so we can start fresh, with no selection on both RadioGroups
rg2.clearCheck();
rg1.setOnCheckedChangeListener(listener1);
rg2.setOnCheckedChangeListener(listener2);

要在這些RadioGroups僅選擇一個RadioButton ,上面的偵聽器將是:

private OnCheckedChangeListener listener1 = new OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            if (checkedId != -1) {
                rg2.setOnCheckedChangeListener(null); // remove the listener before clearing so we don't throw that stackoverflow exception(like Vladimir Volodin pointed out)
                rg2.clearCheck(); // clear the second RadioGroup!
                rg2.setOnCheckedChangeListener(listener2); //reset the listener
                Log.e("XXX2", "do the work");
            }
        }
    };

    private OnCheckedChangeListener listener2 = new OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            if (checkedId != -1) {
                rg1.setOnCheckedChangeListener(null);
                rg1.clearCheck();
                rg1.setOnCheckedChangeListener(listener1);
                Log.e("XXX2", "do the work");
            }
        }
    };

要從RadioGroups獲取選中的RadioButton ,您可以執行以下操作:

int chkId1 = rg1.getCheckedRadioButtonId();
int chkId2 = rg2.getCheckedRadioButtonId();
int realCheck = chkId1 == -1 ? chkId2 : chkId1;

如果您使用RadioGroupcheck()方法,您必須記住在另一個Radiogroup上調用clearCheck()

RadioGroup 是從 LinearLayout 擴展而來的。

線性布局做不到,所以 RadioGroup 做不到。

為什么不自己實現它。

使用 RelativeLayout 來布局子視圖。 並記錄子視圖的狀態。 使用 setLevel 來控制狀態。

祝你好運!。

如果布局不復雜,最好的方法是使用 Single RelativeLayout 而不是多個 Linear Layout。

下面是 2 行的代碼。 第一行有 3 列。 第二行一列。

            <RadioGroup
                android:id="@+id/radio_group"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginBottom="4dp"
                android:layout_marginTop="4dp"
                android:orientation="vertical">

                <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <android.support.v7.widget.AppCompatRadioButton
                        android:id="@+id/r1c1"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentLeft="true"
                        android:layout_alignParentStart="true"
                        android:layout_alignParentTop="true"
                        android:layout_marginRight="8dp"
                        android:gravity="center"
                        android:text="Row 1 Column1" />

                    <android.support.v7.widget.AppCompatRadioButton
                        android:id="@+id/r2c1"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_below="@id/r1c1"
                        android:layout_gravity="left|center_vertical"
                        android:layout_marginRight="8dp"
                        android:layout_weight="1"
                        android:gravity="left|center_vertical"
                        android:text="Row 2 Column 1" />

                    <android.support.v7.widget.AppCompatRadioButton
                        android:id="@+id/r1c2"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginRight="8dp"
                        android:layout_toRightOf="@id/r1c1"
                        android:gravity="center"
                        android:text="Row 1 Column 2"/>

                    <android.support.v7.widget.AppCompatRadioButton
                        android:id="@+id/r1c3"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginRight="8dp"
                        android:layout_toRightOf="@id/r1c2"
                        android:gravity="center"
                        android:text="Row 1 Column 3" />
                </RelativeLayout>
            </RadioGroup>

使用 LinearLayout 在 xml 文件中創建 2 RadioGroup,每個都有 5 RadioButton 並使用 layout_weight 屬性將它們並排放置在屏幕上。 然后為這些廣播組創建偵聽器,如下所示:

rg1 = (RadioGroup) findViewById(R.id.radiogroup1);
rg2 = (RadioGroup) findViewById(R.id.radiogroup2);
rg1.clearCheck();//this is so we can start fresh, with no selection on both RadioGroups
rg2.clearCheck();
rg1.setOnCheckedChangeListener(new OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            // TODO Auto-generated method stub
            if (checkedId != -1) {
                fun2();
            }
        }
    });

    rg2.setOnCheckedChangeListener(new OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            // TODO Auto-generated method stub
            if (checkedId != -1) {
                fun1();
            }
        }
    });

fun1() & fun2() 的定義如下:

public void fun1() {
     rg1.setOnCheckedChangeListener(null);
     rg1.clearCheck();
     rg1.setOnCheckedChangeListener(new OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            fun2();
            Log.v("Inside fun1","fun2");
        }
    });
}

public void fun2() {
     rg2.setOnCheckedChangeListener(null);
     rg2.clearCheck();
     rg2.setOnCheckedChangeListener(new OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            // TODO Auto-generated method stub
            fun1();
            Log.v("Inside fun2","fun1");

        }
    });
}

我不得不做同樣的事情,最后結合了一個 TableLayout 和一個 RadioButtonGroup。 我將單選按鈕動態添加到 TableRows。 這是課程:

public class RadioGroupColumns extends TableLayout implements OnClickListener {

private static final String TAG = "RadioGroupColumns";
private RadioButton activeRadioButton;
private int mCheckedId = -1;
// tracks children radio buttons checked state
private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener;
// when true, mOnCheckedChangeListener discards events
private boolean mProtectFromCheckedChange = false;
private OnCheckedChangeListener mOnCheckedChangeListener;
private PassThroughHierarchyChangeListener mPassThroughListener;

public RadioGroupColumns(Context context) {
    super(context);
    setOrientation(VERTICAL);
    init();
}

public RadioGroupColumns(Context context, AttributeSet attrs) {
            super(context, attrs);
            Resources res = Resources.getSystem();
            int value = 0;
            // retrieve selected radio button as requested by the user in the
            // XML layout file

            TypedArray attributes = null;
            try {
                attributes = context.obtainStyledAttributes(attrs, getAttributes(context), R.attr.radioButtonStyle, 0);
                value = attributes.getResourceId(getAttribute(context), View.NO_ID);
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                Log.d("Exception RadioGroupColumns Construct",e.toString());
                e.printStackTrace();
            }
            catch (ClassNotFoundException e) { 
                Log.d("Exception RadioGroupColumns Construct",e.toString());
                e.printStackTrace();
            }  


            if (value != View.NO_ID) {
                mCheckedId = value;
            }

            //hardcode it to vertical
            //final int index = attributes.getInt(com.android.internal.R.styleable.RadioGroup_orientation, VERTICAL);
            //setOrientation(index);

            attributes.recycle();

            setOrientation(VERTICAL);
            init();
}

@Override
public void onClick(View v) {
    if (v instanceof TableRow) {
        TableRow row = (TableRow)v;
        for (int j=0;j<row.getChildCount();j++) { 
            if (RadioButton.class.isAssignableFrom(row.getChildAt(j).getClass())) {

                ((RadioButton) row.getChildAt(j)).setChecked(true);
                activeRadioButton = (RadioButton) row.getChildAt(j);
            }
        }
    }
    else { 
        final RadioButton rb = (RadioButton) v;

        if (activeRadioButton != null) {
            activeRadioButton.setChecked(false);
        }
        rb.setChecked(true);
        activeRadioButton = rb;
    }

}

private void init() {
    mChildOnCheckedChangeListener = new CheckedStateTracker();
    mPassThroughListener = new PassThroughHierarchyChangeListener();
    super.setOnHierarchyChangeListener(mPassThroughListener);
}

private int getAttribute(Context con) throws ClassNotFoundException, IllegalArgumentException, IllegalAccessException { 
//use reflect to get styleable class.
Field[] alFields = null; 
ArrayList<Integer> alInts = new ArrayList<Integer>();
int R_ID = 0;

for (Class c : android.R.class.getClasses()) { 
     if (c.getName().indexOf("styleable") >= 0) { 
         alFields = Class.forName( con.getPackageName() + ".R$styleable" ).getFields();

     } 
 }
for (Field f : alFields) { 
    Log.d("field name",f.getName());
    if (f.getName().equals("RadioGroup_checkedButton")) { 
        int[] ret = (int[])f.get(null);
        R_ID = ret[0];
    }

}
return R_ID;


}

//gets all RadioGroup R,android.internal.styleable.RadioGroup values
private int[] getAttributes(Context con) throws IllegalAccessException, ClassNotFoundException { 
    //use reflect to get styleable class.
    Field[] alFields = null; 
    ArrayList<Integer> alInts = new ArrayList<Integer>();
    int[] ints = null;
    int count = 0;
    try {
        for (Class c : android.R.class.getClasses()) { 
             if (c.getName().indexOf("styleable") >= 0) { 
                 Log.d("get Class Name Outer", c.getName());
                //use reflection to access the resource class
                 alFields = Class.forName( con.getPackageName() + ".R$styleable" ).getFields();          
             } 

         }
         if (alFields != null) 
             {
                Log.d("field numbers size", String.valueOf(alFields.length));

                for (Field field : alFields) { 

                 Class<?> targetType = field.getType();
                 Log.d("field type", field.getType().toString());
                 if (targetType.equals(Integer.TYPE) && targetType.isPrimitive()) { 
                     //alInts.add((Integer)field);
                     Object objectValue = (Integer)field.getInt(null);
                     //Object objectValue = (Integer)targetType.newInstance(); 
                     alInts.add((Integer)objectValue);
                     count++;
                 }
                 ints = new int[count];
                 for (int i=0;i<alInts.size();i++) { 
                     ints[i] = alInts.get(i);
                 }
             }

         }
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    catch (ClassNotFoundException e) { 
        e.printStackTrace();
    }
    return ints;


    }

public void check(int id) {
    // don't even bother
    if (id != -1 && (id == mCheckedId)) {
        return;
    }
    if (mCheckedId != -1) {
        setCheckedStateForView(mCheckedId, false);
    }
    if (id != -1) {
        setCheckedStateForView(id, true);
    }
    activeRadioButton = (RadioButton) findViewById(id);
    activeRadioButton.setChecked(true);
    setCheckedId(id);
}

public void setOnCheckedChangeListener(RadioGroupColumns.OnCheckedChangeListener onCheckedChangeListener) {
    mOnCheckedChangeListener = (OnCheckedChangeListener) onCheckedChangeListener;
}

private void setCheckedId(int id) {
    mCheckedId = id;
    if (mOnCheckedChangeListener != null) {
        mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
    }
}

private void setCheckedStateForView(int viewId, boolean checked) {
    View checkedView = findViewById(viewId);
    if (!RadioButton.class.isAssignableFrom(checkedView.getClass()) && checkedView != null) { 
        TableRow row = (TableRow) checkedView;
        for (int j=0;j<row.getChildCount();j++) { 
            RadioButton button = (RadioButton) row.getChildAt(j);
            if (button.isChecked() && button != null) { 
                button.setChecked(checked);
            }
        }
    }
    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);
        }
    }
}

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

    return -1;
}

public interface OnCheckedChangeListener {

    public void onCheckedChanged(RadioGroupColumns group, int checkedId);
}

private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener {
    public void onCheckedChanged(CompoundButton buttonView,
            boolean isChecked) {
        // prevents from infinite recursion
        if (mProtectFromCheckedChange) {
            return;
        }

        mProtectFromCheckedChange = true;
        if (mCheckedId != -1) {
            setCheckedStateForView(mCheckedId, false);
        }
        mProtectFromCheckedChange = false;
        int id = buttonView.getId();
        setCheckedId(id);
    }
}

private class PassThroughHierarchyChangeListener implements android.view.ViewGroup.OnHierarchyChangeListener {
    private android.view.ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener;

    /**
     * {@inheritDoc}
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    public void onChildViewAdded(View parent, View child) {
        if (parent == RadioGroupColumns.this
                && child instanceof RadioButton) {
            int id = child.getId();
            // generates an id if it's missing
            if (id == View.NO_ID) {
                id = View.generateViewId();
                child.setId(id);
            }
            ((RadioButton) child).setOnCheckedChangeListener((com.assistek.ediary.RadioButton.OnCheckedChangeListener) mChildOnCheckedChangeListener);
        }

        if (mOnHierarchyChangeListener != null) {
            mOnHierarchyChangeListener.onChildViewAdded(parent, child);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void onChildViewRemoved(View parent, View child) {
        if (parent == RadioGroupColumns.this
                && child instanceof RadioButton) {
            ((RadioButton) child).setOnCheckedChangeListener(null);
        }

        if (mOnHierarchyChangeListener != null) {
            mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
        }
    }
}

 }

這是將單選按鈕添加到擴展無線電組的代碼:

    private void setupRadioButtonAnswers() {
    ArrayList<HolderAnswer> listAnswers = GlobalVars.questionHolders[GlobalVars.arrayRowNumber]
            .getListAnswers();
    ArrayList<ArrayList<HolderAnswer>> listAnswersSorted = new ArrayList<ArrayList<HolderAnswer>>();
    ArrayList<TableRow> alTableRows = new ArrayList<TableRow>();

    int NumberInColumns = (int) Math.floor(listAnswers.size() / NUMBER_OF_COLUMNS);
    // make higher number of answers on the right
    if (listAnswers.size() % NUMBER_OF_COLUMNS > 0)
        NumberInColumns++;

    for (int i = 0; i < NumberInColumns; i++) {
        TableRow row = new TableRow(this);
        TableRow.LayoutParams lp = new TableRow.LayoutParams(
                TableRow.LayoutParams.WRAP_CONTENT);
        row.setLayoutParams(lp);
        alTableRows.add(row);
    }

    int count = 0;

    // sort by row
    /*
     * a[0] = "Question 1"
       a[1] = "Question 2"
       a[2] = "Question 3"
       a[3] = "Question 4"
       a[4] = "Question 5"
       a[5] = "Question 6"
       a[6] = "Question 7"

       sorted to:

       a[0] = "Question 1"   a[1] = "Question 5"
       a[2] = "Question 2"   a[3] = "Question 6"
       a[4] = "Question 3"   a[5] = "Question 7"
       a[6] = "Question 4"
     */

    // initialize the ArrayLists in listAnswersSorted
    int numRows = listAnswers.size() / NUMBER_OF_COLUMNS + 1;
    for (int i = 0; i < numRows; i += 1) {
        listAnswersSorted.add(new ArrayList<HolderAnswer>());
    }

    // calculate column index where the "step" happens
    int step = listAnswers.size() % NUMBER_OF_COLUMNS;

    // loop through and add elements to listAnswersSorted
    int index = 0;
    int row = 0;
    int col = 0;
    while (index < listAnswers.size()) {
        listAnswersSorted.get(row).add(listAnswers.get(index));

        int rows = col < step ? numRows : numRows - 1;
        row += 1;
        if (row == rows) {
        row = 0;
        col += 1;
        }
        index += 1;
    }


    row = 0;
    int columncount = 1;
    for (ArrayList<HolderAnswer> sortedArrayList : listAnswersSorted) {
        for (HolderAnswer answer : sortedArrayList) {

            final RadioButton button = new RadioButton(this);
            button.setTag(answer.getRecID());
            button.setId(GlobalVars.getLatestId());
            button.setTextColor(Color.BLACK);
            GlobalVars.setupText(con, button, answer.getTextID());

            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    button.setEnabled(false);
                    handlerReenableView.sendEmptyMessageDelayed(button.getId(), 1000);

                    button.setChecked(true);
                    radioGroup.check(button.getId());
                }
            });


            button.setLayoutParams(new TableRow.LayoutParams(columncount));
            alTableRows.get(row).addView(button);

            if (columncount==NUMBER_OF_COLUMNS) { 
                columncount = 1;
                radioGroup.addView(alTableRows.get(row));
                alTableRows.get(row).setOnClickListener(new View.OnClickListener() {

                    @Override
                    public void onClick(View v) {

                        for(int k=0;k<((TableRow) v).getChildCount();k++) { 
                            TableRow row = (TableRow) v;
                            for (int l=0;l<row.getChildCount();l++) { 
                                RadioButton tableButton =  (RadioButton) row.getChildAt(l);
                                if (tableButton.isChecked) { 
                                    radioGroup.check(tableButton.getId());
                                }
                            }
                        }
                    }

                });

            }

            else { 
                columncount++;
            }
            //if (row=NumberInColumns)
            count++;

        }

        if (count == listAnswers.size()) { 
            radioGroup.addView(alTableRows.get(row));
        }
        row++;

    }



    radioGroup.setOnCheckedChangeListener(new RadioGroupColumns.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(RadioGroupColumns group, int checkedId) {
                    Log.d("We're here",String.valueOf(checkedId));
                    if (checkedId == -1) {
                        for (int i = 0; i < radioGroup.getChildCount(); i++) {
                            TableRow row = (TableRow)radioGroup.getChildAt(i); 
                            for (int j=0;j<row.getChildCount();j++) { 
                                if (RadioButton.class.isAssignableFrom(row.getChildAt(j).getClass())) {
                                    ((RadioButton) row.getChildAt(j)).setChecked(false);
                                }
                            }
                        }
                    } else {
                        for (int i = 0; i < radioGroup.getChildCount(); i++) {
                            TableRow row = (TableRow)radioGroup.getChildAt(i); 
                            for (int j=0;j<row.getChildCount();j++) { 
                                if (RadioButton.class.isAssignableFrom(row.getChildAt(j).getClass()) 
                                        && row.getChildAt(j).getId() != -1) {
                                    ((RadioButton) row.getChildAt(j)).setChecked(false);
                                }
                            }

                        }

                        RadioButton checkedRadioButton = (RadioButton) radioGroup.findViewById(checkedId);
                        checkedRadioButton.setChecked(true);
                        Log.d("checkedID onchecked Change()", String.valueOf(radioGroup.getCheckedRadioButtonId()));
                    }
                }

            });

}

更新:自從我在這里發布代碼以來,我更新了代碼。 更新的在這個鏈接: https : //github.com/Gavras/MultiLineRadioGroup/blob/master/app/src/main/java/com/whygraphics/multilineradiogroup/MultiLineRadioGroup.java

當我需要多線無線電組時,我做了一些非常小的事情

這是一個擴展 RadioGroup 的自定義視圖。

您可以連續選擇所需的最大按鈕數。 它基於 TableLayout,因此它也對齊按鈕。 一切都有記錄。

/**
 * Layout that arranges radio buttons in multiple lines.
 * Only one radio button can be checked at the same time.
 * <p>
 * XML Attributes:
 * <p>
 * max_in_row:
 * A non-negative number that represents the maximum radio buttons in a row,
 * 0 for all in one line.
 * <p>
 * radio_buttons:
 * String-array resource reference that represents the texts of the desired radio buttons.
 * <p>
 * default_button:
 * String that represents the text or the index of the radio button to be checked by default.
 * The string should be in the following format:
 * for text: "text:[text-of-button]" where text-of-button is the text of the button to check.
 * for index: "index:[index-of-button]" where index-of-button is the index of the button to check.
 * when the prefix omitted, "text:" inserted implicitly.
 */
public class MultiLineRadioGroup extends RadioGroup {

    private static final String XML_DEFAULT_BUTTON_PREFIX_INDEX = "index:";
    private static final String XML_DEFAULT_BUTTON_PREFIX_TEXT = "text:";

    private static final int DEF_VAL_MAX_IN_ROW = 0;

    private OnCheckedChangeListener mOnCheckedChangeListener;

    private int mMaxInRow;

    // all buttons are stored in table layout
    private TableLayout mTableLayout;

    // list to store all the buttons
    private List<RadioButton> mRadioButtons;

    // the checked button
    private RadioButton checkedButton;

    /**
     * Creates a new MultiLineRadioGroup for the given context.
     *
     * @param context the application environment
     */
    public MultiLineRadioGroup(Context context) {
        super(context);
        init(null);
    }

    /**
     * Creates a new MultiLineRadioGroup for the given context
     * and with the specified set attributes.
     *
     * @param context the application environment
     * @param attrs   a collection of attributes
     */
    public MultiLineRadioGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    // initializes the layout
    private void init(AttributeSet attrs) {
        mRadioButtons = new ArrayList<>();

        mTableLayout = getTableLayout();
        addView(mTableLayout);

        if (attrs != null)
            initAttrs(attrs);
    }

    // initializes the layout with the specified attributes
    private void initAttrs(AttributeSet attrs) {
        TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(
                attrs, R.styleable.multi_line_radio_group,
                0, 0);
        try {
            // gets and sets the max in row.
            setMaxInRow(typedArray.getInt(R.styleable.multi_line_radio_group_max_in_row,
                    DEF_VAL_MAX_IN_ROW));

            // gets and adds the starting buttons
            CharSequence[] radioButtonStrings = typedArray.getTextArray(
                    R.styleable.multi_line_radio_group_radio_buttons);
            addButtons(radioButtonStrings);

            // gets the default button and checks it if presents.
            String string = typedArray.getString(R.styleable.multi_line_radio_group_default_button);
            if (string != null)
                setDefaultButton(string);

        } finally {
            typedArray.recycle();
        }
    }

    // checks the default button based on the passed string
    private void setDefaultButton(String string) {
        final int START_OF_INDEX = 6;
        final int START_OF_TEXT = 5;

        // the text of the button to check
        String buttonToCheck;

        if (string.startsWith(XML_DEFAULT_BUTTON_PREFIX_INDEX)) {
            String indexString = string.substring(START_OF_INDEX, string.length());
            int index = Integer.parseInt(indexString);
            if (index < 0 || index >= mRadioButtons.size())
                throw new IllegalArgumentException("index must be between 0 to getRadioButtonCount() - 1 [" +
                        (getRadioButtonCount() - 1) + "]");
            buttonToCheck = mRadioButtons.get(index).getText().toString();

        } else if (string.startsWith(XML_DEFAULT_BUTTON_PREFIX_TEXT)) {
            buttonToCheck = string.substring(START_OF_TEXT, string.length());

        } else { // when there is no prefix assumes the string is the text of the button
            buttonToCheck = string;
        }

        check(buttonToCheck);
    }

    /**
     * Returns the table layout to set to this layout.
     *
     * @return the table layout
     */
    protected TableLayout getTableLayout() {
        return (TableLayout) LayoutInflater.from(getContext())
                .inflate(R.layout.table_layout, this, false);
    }

    /**
     * Returns the table row to set in this layout.
     *
     * @return the table row
     */
    protected TableRow getTableRow() {
        return (TableRow) LayoutInflater.from(getContext())
                .inflate(R.layout.table_row, mTableLayout, false);
    }

    /**
     * Returns the radio button to set in this layout.
     *
     * @return the radio button
     */
    protected RadioButton getRadioButton() {
        return (RadioButton) LayoutInflater.from(getContext())
                .inflate(R.layout.radio_button, null);
    }

    /**
     * Register a callback to be invoked when a radio button is checked.
     *
     * @param onCheckedChangeListener the listener to attach
     */
    public void setOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) {
        this.mOnCheckedChangeListener = onCheckedChangeListener;
    }

    /**
     * Sets the maximum radio buttons in a row, 0 for all in one line
     * and arranges the layout accordingly.
     *
     * @param maxInRow the maximum radio buttons in a row
     * @throws IllegalArgumentException if maxInRow is negative
     */
    public void setMaxInRow(int maxInRow) {
        if (maxInRow < 0)
            throw new IllegalArgumentException("maxInRow must not be negative");
        this.mMaxInRow = maxInRow;
        arrangeButtons();
    }

    /**
     * Adds a view to the layout
     * <p>
     * Consider using addButtons() instead
     *
     * @param child the view to add
     */
    @Override
    public void addView(View child) {
        addView(child, -1, child.getLayoutParams());
    }

    /**
     * Adds a view to the layout in the specified index
     * <p>
     * Consider using addButtons() instead
     *
     * @param child the view to add
     * @param index the index in which to insert the view
     */
    @Override
    public void addView(View child, int index) {
        addView(child, index, child.getLayoutParams());
    }

    /**
     * Adds a view to the layout with the specified width and height.
     * Note that for radio buttons the width and the height are ignored.
     * <p>
     * Consider using addButtons() instead
     *
     * @param child  the view to add
     * @param width  the width of the view
     * @param height the height of the view
     */
    @Override
    public void addView(View child, int width, int height) {
        addView(child, -1, new LinearLayout.LayoutParams(width, height));
    }

    /**
     * Adds a view to the layout with the specified layout params.
     * Note that for radio buttons the params are ignored.
     * <p>
     * Consider using addButtons() instead
     *
     * @param child  the view to add
     * @param params the layout params of the view
     */
    @Override
    public void addView(View child, ViewGroup.LayoutParams params) {
        addView(child, -1, params);
    }

    /**
     * Adds a view to the layout in the specified index
     * with the specified layout params.
     * Note that for radio buttons the params are ignored.
     * <p>
     * * Consider using addButtons() instead
     *
     * @param child  the view to add
     * @param index  the index in which to insert the view
     * @param params the layout params of the view
     */
    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if (params == null) {
            params = new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        }

        if (child instanceof RadioButton)
            addButtons(index, ((RadioButton) child).getText());
        else
            super.addView(child, index, params);
    }

    /**
     * Adds radio buttons to the layout based on the texts in the radioButtons array.
     * Adds them in the last index.
     * If radioButtons is null does nothing.
     *
     * @param radioButtons the texts of the buttons to add
     */
    public void addButtons(CharSequence... radioButtons) {
        addButtons(-1, radioButtons);
    }

    /**
     * Adds radio buttons to the layout based on the texts in the radioButtons array.
     * Adds them in the specified index, -1 for the last index.
     * If radioButtons is null does nothing.
     *
     * @param index        the index in which to insert the radio buttons
     * @param radioButtons the texts of the buttons to add
     * @throws IllegalArgumentException if index is less than -1 or greater than the number of radio buttons
     */
    public void addButtons(int index, CharSequence... radioButtons) {
        if (index < -1 || index > mRadioButtons.size())
            throw new IllegalArgumentException("index must be between -1 to getRadioButtonCount() [" +
                    getRadioButtonCount() + "]");

        if (radioButtons == null)
            return;

        int realIndex = (index != -1) ? index : mRadioButtons.size();

        // adds the buttons to the list
        for (CharSequence text : radioButtons)
            mRadioButtons.add(realIndex++, createRadioButton(text));

        arrangeButtons();
    }

    // creates a radio button with the specified text
    private RadioButton createRadioButton(CharSequence text) {
        RadioButton radioButton = getRadioButton();
        radioButton.setText(text);
        radioButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                checkButton((RadioButton) v);

                if (mOnCheckedChangeListener != null)
                    mOnCheckedChangeListener.onCheckedChanged(MultiLineRadioGroup.this, checkedButton);
            }
        });
        return radioButton;
    }

    /**
     * Removes a view from the layout.
     * <p>
     * Consider using removeButton().
     *
     * @param view the view to remove
     */
    @Override
    public void removeView(View view) {
        super.removeView(view);
    }

    /**
     * Removes a view from the layout in the specified index.
     * <p>
     * Consider using removeButton().
     *
     * @param index the index from which to remove the view
     */
    @Override
    public void removeViewAt(int index) {
        super.removeViewAt(index);
    }

    /**
     * Removes the specified range of views from the layout.
     * <p>
     * Consider using removeButtons().
     *
     * @param start the start index to remove
     * @param count the number of views to remove
     */
    @Override
    public void removeViews(int start, int count) {
        super.removeViews(start, count);
    }

    /**
     * Removes all the views from the layout.
     * <p>
     * Consider using removeAllButtons().
     */
    @Override
    public void removeAllViews() {
        super.removeAllViews();
    }

    /**
     * Removes a radio button from the layout.
     * If the radio button is null does nothing.
     *
     * @param radioButton the radio button to remove
     */
    public void removeButton(RadioButton radioButton) {
        if (radioButton == null)
            return;

        removeButton(radioButton.getText());
    }

    /**
     * Removes a radio button from the layout based on its text.
     * Removes the first occurrence.
     * If the text is null does nothing.
     *
     * @param text the text of the radio button to remove
     */
    public void removeButton(CharSequence text) {
        if (text == null)
            return;

        int index = -1;

        for (int i = 0, len = mRadioButtons.size(); i < len; i++) {
            // checks if the texts are equal
            if (mRadioButtons.get(i).getText().equals(text)) {
                index = i;
                break;
            }
        }

        // removes just if the index was found
        if (index != -1)
            removeButton(index);
    }

    /**
     * Removes the radio button in the specified index from the layout.
     *
     * @param index the index from which to remove the radio button
     * @throws IllegalArgumentException if index is less than 0
     *                                  or greater than the number of radio buttons - 1
     */
    public void removeButton(int index) {
        removeButtons(index, 1);
    }

    /**
     * Removes all the radio buttons in the specified range from the layout.
     * Count can be any non-negative number.
     *
     * @param start the start index to remove
     * @param count the number of radio buttons to remove
     * @throws IllegalArgumentException if index is less than 0
     *                                  or greater than the number of radio buttons - 1
     *                                  or count is negative
     */
    public void removeButtons(int start, int count) {
        if (start < 0 || start >= mRadioButtons.size())
            throw new IllegalArgumentException("remove index must be between 0 to getRadioButtonCount() - 1 [" +
                    (getRadioButtonCount() - 1) + "]");

        if (count < 0)
            throw new IllegalArgumentException("count must not be negative");

        if (count == 0)
            return;

        int endIndex = start + count - 1;
        // if endIndex is not in the range of the radio buttons sets it to the last index
        if (endIndex >= mRadioButtons.size())
            endIndex = mRadioButtons.size() - 1;

        // iterates over the buttons to remove
        for (int i = endIndex; i >= start; i--) {
            RadioButton radiobutton = mRadioButtons.get(i);
            // if the button to remove is the checked button set checkedButton to null
            if (radiobutton == checkedButton)
                checkedButton = null;
            // removes the button from the list
            mRadioButtons.remove(i);
        }

        arrangeButtons();
    }

    /**
     * Removes all the radio buttons from the layout.
     */
    public void removeAllButtons() {
        removeButtons(0, mRadioButtons.size());
    }

    // arrange the button in the layout
    private void arrangeButtons() {
        // iterates over each button and puts it in the right place
        for (int i = 0, len = mRadioButtons.size(); i < len; i++) {
            RadioButton radioButtonToPlace = mRadioButtons.get(i);
            int rowToInsert = (mMaxInRow != 0) ? i / mMaxInRow : 0;
            int columnToInsert = (mMaxInRow != 0) ? i % mMaxInRow : i;
            // gets the row to insert. if there is no row create one
            TableRow tableRowToInsert = (mTableLayout.getChildCount() <= rowToInsert)
                    ? addTableRow() : (TableRow) mTableLayout.getChildAt(rowToInsert);
            int tableRowChildCount = tableRowToInsert.getChildCount();

            // if there is already a button in the position
            if (tableRowChildCount > columnToInsert) {
                RadioButton currentButton = (RadioButton) tableRowToInsert.getChildAt(columnToInsert);

                // insert the button just if the current button is different
                if (currentButton != radioButtonToPlace) {
                    // removes the current button
                    removeButtonFromParent(currentButton, tableRowToInsert);
                    // removes the button to place from its current position
                    removeButtonFromParent(radioButtonToPlace, (ViewGroup) radioButtonToPlace.getParent());
                    // adds the button to the right place
                    tableRowToInsert.addView(radioButtonToPlace, columnToInsert);
                }

                // if there isn't already a button in the position
            } else {
                // removes the button to place from its current position
                removeButtonFromParent(radioButtonToPlace, (ViewGroup) radioButtonToPlace.getParent());
                // adds the button to the right place
                tableRowToInsert.addView(radioButtonToPlace, columnToInsert);
            }
        }

        removeRedundancies();
    }

    // removes the redundant rows and radio buttons
    private void removeRedundancies() {
        // the number of rows to fit the buttons
        int rows;
        if (mRadioButtons.size() == 0)
            rows = 0;
        else if (mMaxInRow == 0)
            rows = 1;
        else
            rows = (mRadioButtons.size() - 1) / mMaxInRow + 1;

        int tableChildCount = mTableLayout.getChildCount();
        // if there are redundant rows remove them
        if (tableChildCount > rows)
            mTableLayout.removeViews(rows, tableChildCount - rows);

        tableChildCount = mTableLayout.getChildCount();
        int maxInRow = (mMaxInRow != 0) ? mMaxInRow : mRadioButtons.size();

        // iterates over the rows
        for (int i = 0; i < tableChildCount; i++) {
            TableRow tableRow = (TableRow) mTableLayout.getChildAt(i);
            int tableRowChildCount = tableRow.getChildCount();

            int startIndexToRemove;
            int count;

            // if it is the last row removes all redundancies after the last button in the list
            if (i == tableChildCount - 1) {
                startIndexToRemove = (mRadioButtons.size() - 1) % maxInRow + 1;
                count = tableRowChildCount - startIndexToRemove;

                // if it is not the last row removes all the buttons after maxInRow position
            } else {
                startIndexToRemove = maxInRow;
                count = tableRowChildCount - maxInRow;
            }

            if (count > 0)
                tableRow.removeViews(startIndexToRemove, count);
        }
    }

    // adds and returns a table row
    private TableRow addTableRow() {
        TableRow tableRow = getTableRow();
        mTableLayout.addView(tableRow);
        return tableRow;
    }

    // removes a radio button from a parent
    private void removeButtonFromParent(RadioButton radioButton, ViewGroup parent) {
        if (radioButton == null || parent == null)
            return;

        parent.removeView(radioButton);
    }

    /**
     * Returns the number of radio buttons.
     *
     * @return the number of radio buttons
     */
    public int getRadioButtonCount() {
        return mRadioButtons.size();
    }

    /**
     * Returns the radio button in the specified index.
     * If the index is out of range returns null.
     *
     * @param index the index of the radio button
     * @return the radio button
     */
    public RadioButton getRadioButtonAt(int index) {
        if (index < 0 || index >= mRadioButtons.size())
            return null;

        return mRadioButtons.get(index);
    }

    /**
     * Checks the radio button with the specified id.
     * If the specified id is not found does nothing.
     *
     * @param id the radio button's id
     */
    @Override
    public void check(int id) {
        if (id <= 0)
            return;

        for (RadioButton radioButton : mRadioButtons) {
            if (radioButton.getId() == id) {
                checkButton(radioButton);
                return;
            }
        }
    }

    /**
     * Checks the radio button with the specified text.
     * If there is more than one radio button associated with this text
     * checks the first radio button.
     * If the specified text is not found does nothing.
     *
     * @param text the radio button's text
     */
    public void check(CharSequence text) {
        if (text == null)
            return;

        for (RadioButton radioButton : mRadioButtons) {
            if (radioButton.getText().equals(text)) {
                checkButton(radioButton);
                return;
            }
        }
    }

    /**
     * Checks the radio button at the specified index.
     * If the specified index is invalid does nothing.
     *
     * @param index the radio button's index
     */
    public void checkAt(int index) {
        if (index < 0 || index >= mRadioButtons.size())
            return;

        checkButton(mRadioButtons.get(index));
    }

    // checks and switches the button with the checkedButton
    private void checkButton(RadioButton button) {
        if (button == null)
            return;

        // if the button to check is different from the current checked button
        if (button != checkedButton) {

            // if exists sets checkedButton to null
            if (checkedButton != null)
                checkedButton.setChecked(false);

            button.setChecked(true);
            checkedButton = button;
        }
    }

    /**
     * Clears the checked radio button
     */
    @Override
    public void clearCheck() {
        checkedButton.setChecked(false);
        checkedButton = null;
    }

    /**
     * Returns the checked radio button's id.
     * If no radio buttons are checked returns -1.
     *
     * @return the checked radio button's id
     */
    @Override
    public int getCheckedRadioButtonId() {
        if (checkedButton == null)
            return -1;

        return checkedButton.getId();
    }

    /**
     * Returns the checked radio button's index.
     * If no radio buttons are checked returns -1.
     *
     * @return the checked radio button's index
     */
    public int getCheckedRadioButtonIndex() {
        if (checkedButton == null)
            return -1;

        return mRadioButtons.indexOf(checkedButton);
    }

    /**
     * Returns the checked radio button's text.
     * If no radio buttons are checked returns null.
     *
     * @return the checked radio buttons's text
     */
    public CharSequence getCheckedRadioButtonText() {
        if (checkedButton == null)
            return null;

        return checkedButton.getText();
    }

    /**
     * Interface definition for a callback to be invoked when a radio button is checked.
     */
    public interface OnCheckedChangeListener {
        /**
         * Called when a radio button is checked.
         *
         * @param group  the MultiLineRadioGroup that stores the radio button
         * @param button the radio button that was checked
         */
        void onCheckedChanged(MultiLineRadioGroup group, RadioButton button);
    }
}

值/attrs.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="multi_line_radio_group">
        <attr name="max_in_row" format="integer" />
        <attr name="radio_buttons" format="reference" />
        <attr name="default_button" format="string" />
    </declare-styleable>
</resources>

R.layout.table_layout:

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/table_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:stretchColumns="*" />

R.layout.table_row:

<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/table_row"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

R.layout.radio_button:(你可以在這里改變文字大小)

<?xml version="1.0" encoding="utf-8"?>
<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/radio_button"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:textSize="@dimen/radio_button_text_size" />

從 xml 使用此布局的示例:

<?xml version="1.0" encoding="utf-8"?>
<[package].MultiLineRadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:multi_line_radio_group="http://schemas.android.com/apk/res-auto"
    android:id="@+id/multi_line_radio_group"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    multi_line_radio_group:default_button="@string/defaultText"
    multi_line_radio_group:max_in_row="@integer/radio_button_max_in_row"
    multi_line_radio_group:radio_buttons="@array/radio_buttons" />

我創建了自己的 RadioGridLayout,其中包括 RadioGroup 代碼並擴展了 GridLayout。 您可以復制此代碼。 對我來說工作得很好。 在您可以在您的 xml 中使用此布局之后。 並像網格布局一樣自定義。

對於 R.styleable.RadioGridLayout_checked 我使用了這樣的代碼:

<resources>
    <declare-styleable name="RadioGridLayout">
        <attr name="checked" format="integer" />
    </declare-styleable>
</resources>
public class RadioGridLayout extends GridLayout {

    private int mCheckedId = -1;
    private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener;
    private boolean mProtectFromCheckedChange = false;
    private OnCheckedChangeListener mOnCheckedChangeListener;
    private PassThroughHierarchyChangeListener mPassThroughListener;

    private void setCheckedId(@IdRes int id) {
        mCheckedId = id;
        if (mOnCheckedChangeListener != null) {
            mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
        }
        AutofillManager afm = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            afm = getContext().getSystemService(AutofillManager.class);
        }
        if (afm != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                afm.notifyValueChanged(this);
            }
        }
    }

    public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
        mOnCheckedChangeListener = listener;
    }

    public interface OnCheckedChangeListener {
        void onCheckedChanged(RadioGridLayout group, @IdRes int checkedId);
    }

    private int mInitialCheckedId = View.NO_ID;

    public RadioGridLayout(Context context) {
        super(context);
        setOrientation(VERTICAL);
        init();
    }

    public RadioGridLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
                setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
            }
        }

        TypedArray attributes = context.obtainStyledAttributes(
                attrs,
                R.styleable.RadioGridLayout,
                R.attr.radioButtonStyle, 0);

        int value = attributes.getResourceId(R.styleable.RadioGridLayout_checked, View.NO_ID);
        if (value != View.NO_ID) {
            mCheckedId = value;
            mInitialCheckedId = value;
        }

        attributes.recycle();
        init();
    }

    private void init() {
        mChildOnCheckedChangeListener = new CheckedStateTracker();
        mPassThroughListener = new PassThroughHierarchyChangeListener();
        super.setOnHierarchyChangeListener(mPassThroughListener);
    }

    @Override
    public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
        mPassThroughListener.mOnHierarchyChangeListener = listener;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (mCheckedId != -1) {
            mProtectFromCheckedChange = true;
            setCheckedStateForView(mCheckedId, true);
            mProtectFromCheckedChange = false;
            setCheckedId(mCheckedId);
        }
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if (child instanceof RadioButton) {
            final RadioButton button = (RadioButton) child;
            if (button.isChecked()) {
                mProtectFromCheckedChange = true;
                if (mCheckedId != -1) {
                    setCheckedStateForView(mCheckedId, false);
                }
                mProtectFromCheckedChange = false;
                setCheckedId(button.getId());
            }
        }

        super.addView(child, index, params);
    }

    public void check(@IdRes int id) {
        if (id != -1 && (id == mCheckedId)) {
            return;
        }

        if (mCheckedId != -1) {
            setCheckedStateForView(mCheckedId, false);
        }

        if (id != -1) {
            setCheckedStateForView(id, true);
        }

        setCheckedId(id);
    }

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

    @IdRes
    public int getCheckedRadioButtonId() {
        return mCheckedId;
    }

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

    @Override
    public GridLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new GridLayout.LayoutParams(getContext(), attrs);
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof RadioGroup.LayoutParams;
    }

    @Override
    protected GridLayout.LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams();
    }

    @Override
    public CharSequence getAccessibilityClassName() {
        return RadioGroup.class.getName();
    }

    public static class LayoutParams extends GridLayout.LayoutParams {

        public LayoutParams(Spec rowSpec, Spec columnSpec) {
            super(rowSpec, columnSpec);
        }

        public LayoutParams() {
            super();
        }

        public LayoutParams(ViewGroup.LayoutParams params) {
            super(params);
        }

        public LayoutParams(MarginLayoutParams params) {
            super(params);
        }

        public LayoutParams(GridLayout.LayoutParams source) {
            super(source);
        }

        public LayoutParams(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        @Override
        protected void setBaseAttributes(TypedArray a,
                                         int widthAttr, int heightAttr) {

            if (a.hasValue(widthAttr)) {
                width = a.getLayoutDimension(widthAttr, "layout_width");
            } else {
                width = WRAP_CONTENT;
            }

            if (a.hasValue(heightAttr)) {
                height = a.getLayoutDimension(heightAttr, "layout_height");
            } else {
                height = WRAP_CONTENT;
            }
        }
    }

    private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (mProtectFromCheckedChange) {
                return;
            }

            mProtectFromCheckedChange = true;
            if (mCheckedId != -1) {
                setCheckedStateForView(mCheckedId, false);
            }
            mProtectFromCheckedChange = false;

            int id = buttonView.getId();
            setCheckedId(id);
        }
    }

    private class PassThroughHierarchyChangeListener implements
            ViewGroup.OnHierarchyChangeListener {
        private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener;

        @Override
        public void onChildViewAdded(View parent, View child) {
            if (parent == RadioGridLayout.this && child instanceof RadioButton) {
                int id = child.getId();
                if (id == View.NO_ID) {
                    id = View.generateViewId();
                    child.setId(id);
                }
                ((RadioButton) child).setOnCheckedChangeListener(
                        mChildOnCheckedChangeListener);
            }

            if (mOnHierarchyChangeListener != null) {
                mOnHierarchyChangeListener.onChildViewAdded(parent, child);
            }
        }

        @Override
        public void onChildViewRemoved(View parent, View child) {
            if (parent == RadioGridLayout.this && child instanceof RadioButton) {
                ((RadioButton) child).setOnCheckedChangeListener(null);
            }

            if (mOnHierarchyChangeListener != null) {
                mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
            }
        }
    }

    @Override
    public void onProvideAutofillStructure(ViewStructure structure, int flags) {
        super.onProvideAutofillStructure(structure, flags);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            structure.setDataIsSensitive(mCheckedId != mInitialCheckedId);
        }
    }

    @Override
    public void autofill(AutofillValue value) {
        if (!isEnabled()) return;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (!value.isList()) {
                Timber.w(value + " could not be autofilled into " + this);
                return;
            }
        }

        int index = 0;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            index = value.getListValue();
        }
        final View child = getChildAt(index);
        if (child == null) {
            Timber.w("RadioGroup.autoFill(): no child with index %s", index);
            return;
        }

        check(child.getId());
    }

    @Override
    public int getAutofillType() {
        return isEnabled() ? AUTOFILL_TYPE_LIST : AUTOFILL_TYPE_NONE;
    }

    @Override
    public AutofillValue getAutofillValue() {
        if (!isEnabled()) return null;

        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getId() == mCheckedId) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    return AutofillValue.forList(i);
                }
            }
        }
        return null;
    }
}

其他幾個答案工作正常,但比簡單情況所需的要復雜。 如果您只是希望多個 RadioGroup 成為一個並可以在單擊時處理所有決策,那么您可以這樣做:

在您的布局 XML 中,將相同的點擊處理程序添加到您想要組合的所有RadioButtons:

android:onClick="handleCombinedClick"

然后,讓你的點擊處理程序看起來像這樣:

public void handleCombinedClick(View view) {
    // Clear any checks from both groups:
    rg1.clearCheck();
    rg2.clearCheck();

    // Manually set the check in the newly clicked radio button:
    ((RadioButton) view).setChecked(true);

    // Perform any action desired for the new selection:
    switch (view.getId()) {
        case R.id.radio_button_1:
            // do something
            break;

        case R.id.radio_button_2:
            // do something
            break;

        ...
    }
}

這還有一個額外的好處,那就是讓您在同一地點處理所有選擇。 如果你想把它擴展到 3 個或更多 RadioGroups,那么你只需要添加一個額外的rgX.clearCheck(); 每個添加的組的行。

我相信你的問題現在已經得到了回答,但這里有另一種看法。 使用此代碼,您可以將單選按鈕包裝成您想要的任何布局(實際上您根本不需要單選組)。 此外,我建議使用線性布局來制作您需要的列/行。

我的代碼基於@infografnet 和@lostdev(也感謝@Neromancer 的復合按鈕建議!)

public class AdvRadioGroup {
    public interface OnButtonCheckedListener {
        void onButtonChecked(CompoundButton button);
    }

    private final List<CompoundButton> buttons;
    private final View.OnClickListener onClick = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            setChecked((CompoundButton) v);
        }
    };

    private OnButtonCheckedListener listener;
    private CompoundButton lastChecked;


    public AdvRadioGroup(View view) {
        buttons = new ArrayList<>();
        parseView(view);
    }

    private void parseView(final View view) {
        if(view instanceof CompoundButton) {
            buttons.add((CompoundButton) view);
            view.setOnClickListener(onClick);
        } else if(view instanceof ViewGroup) {
            final ViewGroup group = (ViewGroup) view;
            for (int i = 0; i < group.getChildCount();i++) {
                parseView(group.getChildAt(i));
            }
        }
    }

    public List<CompoundButton> getButtons() { return buttons; }

    public CompoundButton getLastChecked() { return lastChecked; }

    public void setChecked(int index) { setChecked(buttons.get(index)); }

    public void setChecked(CompoundButton button) {
        if(button == lastChecked) return;

        for (CompoundButton btn : buttons) {
            btn.setChecked(false);
        }

        button.setChecked(true);

        lastChecked = button;

        if(listener != null) {
            listener.onButtonChecked(button);
        }
    }

    public void setOnButtonCheckedListener(OnButtonCheckedListener listener) { this.listener = listener; }
}

用法(包括監聽器):

AdvRadioGroup group = new AdvRadioGroup(findViewById(R.id.YOUR_VIEW));
group.setOnButtonCheckedListener(new AdvRadioGroup.OnButtonCheckedListener() {
    @Override
    public void onButtonChecked(CompoundButton button) {
        // do fun stuff here!
    }
});

獎勵:您可以獲得最后一個選中的按鈕,整個按鈕的列表,並且您可以通過索引檢查任何按鈕!

這是我在我的 XML 布局上所做的,它工作正常。

  <RadioGroup
                android:id="@+id/radioGroup"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="2dp">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal">

                <RadioButton
                    android:id="@+id/radioOwner"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:buttonTint="@color/login_button_color"
                    android:fontFamily="@font/rnhousesans_regular"
                    android:text="Owner"
                    android:textColor="@color/colorPrimary" />

                <RadioButton
                    android:id="@+id/radioLivingParents"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:buttonTint="@color/login_button_color"
                    android:fontFamily="@font/rnhousesans_regular"
                    android:text="Living with parents"
                    android:textColor="@color/colorPrimary" />

                </LinearLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal">

                <RadioButton
                    android:id="@+id/radioTenant"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:buttonTint="@color/login_button_color"
                    android:fontFamily="@font/rnhousesans_regular"
                    android:text="Tenant"
                    android:textColor="@color/colorPrimary" />

                <RadioButton
                    android:id="@+id/radioOther"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:buttonTint="@color/login_button_color"
                    android:fontFamily="@font/rnhousesans_regular"
                    android:text="Other"
                    android:textColor="@color/colorPrimary" />

                </LinearLayout>
            </RadioGroup>

您可以在 RadioGroup 中使用嵌套的 GridLayout,盡管您將失去 RadioGroup 的主要屬性來管理 RadioButtons:例如單個項目選擇。 RadioButtons 應該是直接的孩子。

<RadioGroup
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    >

    <GridLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:columnCount="2"
        >

    <androidx.appcompat.widget.AppCompatRadioButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text 1"
        />

    <androidx.appcompat.widget.AppCompatRadioButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text 2"
        />

    <androidx.appcompat.widget.AppCompatRadioButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text 3"
        />

    <androidx.appcompat.widget.AppCompatRadioButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text 4"
        />

    <androidx.appcompat.widget.AppCompatRadioButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text 5"
        />

    </GridLayout>
</RadioGroup>

此解決方案用於處理單擊主體內的單擊

xml :

             <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="horizontal"
                        android:weightSum="2">

                        <RadioGroup
                            android:id="@+id/radioGroup_action_1"
                            android:layout_width="0dp"
                            android:layout_height="wrap_content"
                            android:layout_marginStart="16dp"
                            android:layout_marginTop="16dp"
                            android:layout_marginEnd="16dp"
                            android:layout_marginBottom="16dp"
                            android:layout_weight="1"
                            android:orientation="vertical">

                            <RadioButton
                                android:id="@+id/radio_a"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text="@string/a"
                                android:textColor="@color/textColor"
                                 />

                            <RadioButton
                                android:id="@+id/radio_b"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text="@string/b"
                                android:textColor="@color/textColor" />


                            <RadioButton
                                android:id="@+id/radio_c"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text="@string/c"
                                android:textColor="@color/textColor" />
                        </RadioGroup>

                        <RadioGroup
                            android:id="@+id/radioGroup_action_2"
                            android:layout_width="0dp"
                            android:layout_height="wrap_content"
                            android:layout_marginStart="16dp"
                            android:layout_marginTop="16dp"
                            android:layout_marginEnd="16dp"
                            android:layout_marginBottom="16dp"
                            android:layout_weight="1"
                            android:orientation="vertical">

                            <RadioButton
                                android:id="@+id/radio_d"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:checked="true"
                                android:text="@string/d"
                                android:textColor="@color/textColor" />

                            <RadioButton
                                android:id="@+id/radio_e"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text="@string/e"
                                android:textColor="@color/textColor" />

                            <RadioButton
                                android:id="@+id/f"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text="@string/f"
                                android:textColor="@color/textColor" />

                        </RadioGroup>
                    </LinearLayout>

代碼 :

 private var isChecking = true

 mBinding.radioGroupAction1.setOnCheckedChangeListener { radioGroup, i ->
        if (i != -1 && isChecking) {
            isChecking = false
            mBinding.radioGroupAction2.clearCheck()
        }
        isChecking = true
        when (i) {
            R.id.a -> {
               Log.e("radioGroupAction:", "a")
            }
            R.id.b -> {
               Log.e("radioGroupAction:", "b")
            }
            R.id.c -> {
               Log.e("radioGroupAction:", "c")
            }
        }
    }
    mBinding.radioGroupAction2.setOnCheckedChangeListener { radioGroup, i ->
        if (i != -1 && isChecking) {
            isChecking = false
            mBinding.radioGroupAction1.clearCheck()
        }
        isChecking = true
        when (i) {
            R.id.d -> {
               Log.e("radioGroupAction:", "d")
            }
            R.id.e -> {
               Log.e("radioGroupAction:", "e")
            }
            R.id.f -> {
               Log.e("radioGroupAction:", "f")
            }
        }
    }

祝你好運

Kotlin 代碼:

package com.test.yourproject.utility

import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.CompoundButton
import androidx.appcompat.widget.AppCompatRadioButton
import java.util.concurrent.atomic.AtomicInteger
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull
import androidx.gridlayout.widget.GridLayout


class RadioGridGroup : GridLayout {
    var checkedCheckableImageButtonId = -1
        private set
    private var mChildOnCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null
    private var mProtectFromCheckedChange = false
    private var mOnCheckedChangeListener: OnCheckedChangeListener? = null
    private var mPassThroughListener: PassThroughHierarchyChangeListener? = null

    constructor(context: Context?) : super(context) {
        init()
    }

    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
        init()
    }

    private fun init() {
        mChildOnCheckedChangeListener = CheckedStateTracker()
        mPassThroughListener = PassThroughHierarchyChangeListener()
        super.setOnHierarchyChangeListener(mPassThroughListener)
    }

    override fun setOnHierarchyChangeListener(listener: OnHierarchyChangeListener?) {
        mPassThroughListener!!.mOnHierarchyChangeListener = listener
    }

    protected override fun onFinishInflate() {
        super.onFinishInflate()
        if (checkedCheckableImageButtonId != -1) {
            mProtectFromCheckedChange = true
            setCheckedStateForView(checkedCheckableImageButtonId, true)
            mProtectFromCheckedChange = false
            setCheckedId(checkedCheckableImageButtonId)
        }
    }

    override fun addView(@NonNull child: View, index: Int, params: ViewGroup.LayoutParams?) {
        if (child is AppCompatRadioButton) {
            val button = child as AppCompatRadioButton
            if (button.isChecked) {
                mProtectFromCheckedChange = true
                if (checkedCheckableImageButtonId != -1) {
                    setCheckedStateForView(checkedCheckableImageButtonId, false)
                }
                mProtectFromCheckedChange = false
                setCheckedId(button.id)
            }
        }
        super.addView(child, index, params)
    }

    fun check(id: Int) {
        if (id != -1 && id == checkedCheckableImageButtonId) {
            return
        }
        if (checkedCheckableImageButtonId != -1) {
            setCheckedStateForView(checkedCheckableImageButtonId, false)
        }
        if (id != -1) {
            setCheckedStateForView(id, true)
        }
        setCheckedId(id)
    }

    private fun setCheckedId(id: Int) {
        checkedCheckableImageButtonId = id
        if (mOnCheckedChangeListener != null) {
            mOnCheckedChangeListener!!.onCheckedChanged(this, checkedCheckableImageButtonId)
        }
    }

    private fun setCheckedStateForView(viewId: Int, checked: Boolean) {
        val checkedView: View = findViewById(viewId)
        if (checkedView != null && checkedView is AppCompatRadioButton) {
            (checkedView as AppCompatRadioButton).isChecked = checked
        }
    }

    fun clearCheck() {
        check(-1)
    }

    fun setOnCheckedChangeListener(listener: OnCheckedChangeListener?) {
        mOnCheckedChangeListener = listener
    }

    override fun onInitializeAccessibilityEvent(@NonNull event: AccessibilityEvent) {
        super.onInitializeAccessibilityEvent(event)
        event.className = RadioGridGroup::class.java.name
    }

    override fun onInitializeAccessibilityNodeInfo(@NonNull info: AccessibilityNodeInfo) {
        super.onInitializeAccessibilityNodeInfo(info)
        info.className = RadioGridGroup::class.java.name
    }

    interface OnCheckedChangeListener {
        fun onCheckedChanged(group: RadioGridGroup?, checkedId: Int)
    }

    private inner class CheckedStateTracker : CompoundButton.OnCheckedChangeListener {
        override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
            if (mProtectFromCheckedChange) {
                return
            }
            mProtectFromCheckedChange = true
            if (checkedCheckableImageButtonId != -1) {
                setCheckedStateForView(checkedCheckableImageButtonId, false)
            }
            mProtectFromCheckedChange = false
            val id = buttonView.id
            setCheckedId(id)
        }
    }

    private inner class PassThroughHierarchyChangeListener :
        OnHierarchyChangeListener {
        var mOnHierarchyChangeListener: OnHierarchyChangeListener? = null
        override fun onChildViewAdded(parent: View, child: View) {
            if (parent === this@RadioGridGroup && child is AppCompatRadioButton) {
                var id: Int = child.getId()
                // generates an id if it's missing
                if (id == View.NO_ID) {
                    id = generateViewId()
                    child.setId(id)
                }
                (child as AppCompatRadioButton).setOnCheckedChangeListener(
                    mChildOnCheckedChangeListener
                )
            }
            mOnHierarchyChangeListener?.onChildViewAdded(parent, child)
        }

        override fun onChildViewRemoved(parent: View, child: View?) {
            if (parent === this@RadioGridGroup && child is AppCompatRadioButton) {
                (child as AppCompatRadioButton).setOnCheckedChangeListener(null)
            }
            mOnHierarchyChangeListener?.onChildViewRemoved(parent, child)
        }
    }

    companion object {
        private val sNextGeneratedId: AtomicInteger = AtomicInteger(1)
        fun generateViewId(): Int {
            while (true) {
                val result: Int = sNextGeneratedId.get()

                // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
                var newValue = result + 1
                if (newValue > 0x00FFFFFF) newValue = 1 // Roll over to 1, not 0.
                if (sNextGeneratedId.compareAndSet(result, newValue)) {
                    return result
                }
            }
        }
    }
}

XML:

<com.test.yourproject.utility.RadioGridGroup
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    xmlns:grid="http://schemas.android.com/apk/res-auto"
                    grid:columnCount="3"
                    android:checkedButton="@+id/radio_button_date_range"
                    grid:useDefaultMargins="true">

                    <androidx.appcompat.widget.AppCompatRadioButton
                        android:layout_height="wrap_content"
                        android:layout_width="wrap_content"
                        android:checked="false"
                        android:text="Text1"
                        grid:layout_columnWeight="1"/>

                    <androidx.appcompat.widget.AppCompatRadioButton
                        android:layout_height="wrap_content"
                        android:layout_width="wrap_content"
                        android:text="Text2"
                        grid:layout_columnWeight="1"/>

                    <androidx.appcompat.widget.AppCompatRadioButton
                        android:text="Text3"
                        android:layout_height="wrap_content"
                        android:layout_width="wrap_content"
                        grid:layout_columnWeight="1"/>

                    <androidx.appcompat.widget.AppCompatRadioButton
                        android:text="Text4"
                        android:layout_height="wrap_content"
                        android:layout_width="wrap_content"
                        grid:layout_columnWeight="1"/>

                    <androidx.appcompat.widget.AppCompatRadioButton
                        android:text="Text5"
                        android:layout_height="wrap_content"
                        android:layout_width="wrap_content"
                        grid:layout_columnWeight="1"/>

                    <androidx.appcompat.widget.AppCompatRadioButton
                        android:text="Text6"
                        android:layout_height="wrap_content"
                        android:layout_width="wrap_content"
                        grid:layout_columnWeight="1"/>

                    <androidx.appcompat.widget.AppCompatRadioButton
                        android:text="Text7"
                        android:layout_height="wrap_content"
                        android:layout_width="wrap_content"
                        grid:layout_columnWeight="1"/>

                    <androidx.appcompat.widget.AppCompatRadioButton
                        android:text="Text8"
                        android:layout_height="wrap_content"
                        android:layout_width="wrap_content"
                        grid:layout_columnWeight="1"/>

                    <androidx.appcompat.widget.AppCompatRadioButton
                        android:text="Text9"
                        android:layout_height="wrap_content"
                        android:layout_width="wrap_content"
                        grid:layout_columnWeight="1"/>

                </com.test.yourproject.utility.RadioGridGroup>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM