【问题标题】:RadioGroup with two columns which have ten RadioButtonsRadioGroup 有两列有十个 RadioButtons
【发布时间】:2012-05-12 15:00:10
【问题描述】:

我有一个RadioGroup,我想在两列五行中将按钮彼此相邻对齐,但我无法实现。我尝试过的事情:

  1. RelativeLayout -> 外部RadioGroup -> 内部RadioGroup。 所有RadioButtons 都被选中,但我只想选中一个。
  2. RadioGroup:方向
  3. 跨度,拉伸柱
  4. TableRow
  5. TableLayout

请告诉我如何创建一个 RadioGroup 并在其中包含两列和多个 RadioButtons

【问题讨论】:

标签: android radio-button radio-group


【解决方案1】:

您可以模拟 RadioGroup 以使其看起来只有一个。例如,您有 rg1rg2RadioGroups 方向设置为 vertical(两列))。设置那些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()

【讨论】:

  • Thaaks Luksprog.Awesome 解决方案。
  • 这会导致堆栈溢出(
  • 不,我的设置和你的一样简单。问题是 clearCheck() 总是导致调用 onCheckedChangeListener()。
  • 应该可以,我自己修复了它,只使用了一个布尔标志而不是取消设置监听器。
  • 荒谬的是,我们必须经历这一切,才能拥有 2 列以上的单选按钮。
【解决方案2】:

RadioGroup 是从 LinearLayout 扩展而来的。

linearlayout做不到,所以RadioGroup做不到。

为什么不自己实现呢。

使用RelativeLayout 来布局子视图。 并记录子视图的状态。 使用 setLevel 来控制状态。

祝你好运!

【讨论】:

    【解决方案3】:

    如果布局不复杂,最好的方法是使用 Single RelativeLayout 而不是多个 Linear Layouts。

    下面是 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>
    

    【讨论】:

    • 这行不通。如果我选中任何单选按钮,那么其他单选按钮不会自动取消选中。
    • 没有用。它允许单选按钮的位置可调。但是在选择时,selectionid 返回 -1(未选中)。它允许检查所有按钮,即使它们属于同一个单选组。
    【解决方案4】:

    使用 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");
    
            }
        });
    }
    

    【讨论】:

    • 不起作用!更改该组中的单选按钮时,不会点击单击侦听器上的 rg1。首先需要在 rg2 中点击一个单选按钮,然后才能点击一个听众。
    【解决方案5】:

    我不得不做同样的事情并最终将 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()));
                        }
                    }
    
                });
    
    }
    

    【讨论】:

      【解决方案6】:

      更新: 自从我在这里发布代码以来,我更新了代码。更新的在此链接中: 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" />
      

      【讨论】:

        【解决方案7】:

        我创建了自己的 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;
            }
        }
        

        【讨论】:

          【解决方案8】:

          其他几个答案工作正常,但比简单情况所需的要复杂。如果您只是希望多个 RadioGroup 充当一个并且可以在单击时处理所有决策,那么您可以这样做:

          在您的布局 XML 中,将相同的点击处理程序添加到您想要组合的 所有 单选按钮:

          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(); 行。

          【讨论】:

            【解决方案9】:

            我确定您的问题现在已经得到解答,但这里有另一种看法。使用此代码,您可以将单选按钮包装到您想要的任何布局中(实际上您根本不需要单选组)。另外我建议使用线性布局来制作您需要的列/行。

            我的代码基于@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!
                }
            });
            

            奖励:您可以获得最后检查的按钮,整个按钮的列表,您可以通过索引检查任何按钮!

            【讨论】:

              【解决方案10】:

              这是我在我的 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>
              

              【讨论】:

                【解决方案11】:

                您可以在 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>
                

                【讨论】:

                  【解决方案12】:

                  在点击正文中处理点击的这个解决方案

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

                  祝你好运

                  【讨论】:

                    猜你喜欢
                    • 2012-05-05
                    • 2013-06-14
                    • 1970-01-01
                    • 2017-01-14
                    • 1970-01-01
                    • 2016-04-07
                    • 1970-01-01
                    • 2013-07-15
                    相关资源
                    最近更新 更多