Как сгруппировать RadioButton из разных LinearLayout?

93

Мне было интересно, можно ли сгруппировать каждый сингл RadioButtonв уникальную, RadioGroup сохраняя ту же структуру. Моя структура выглядит так:

  • LinearLayout_main
    • LinearLayout_1
      • RadioButton1
    • LinearLayout_2
      • RadioButton2
    • LinearLayout_3
      • RadioButton3

Как видите, теперь все RadioButtonдети разные LinearLayout. Я пробовал использовать приведенную ниже структуру, но она не работает:

  • Радиогруппа
    • LinearLayout_main
      • LinearLayout_1
        • RadioButton1
      • LinearLayout_2
        • RadioButton2
      • LinearLayout_3
        • RadioButton3
marcoqf73
источник
14
@coding crow, если вы вынуждены спросить, значит, вы никогда не работали с дизайнером UI-потока (и я предполагаю, что ваши переключатели, вероятно, не очень сложные). Представьте (если можете) радиокнопку, которая находится рядом с двумя частями текста, одна из которых является заголовком, а другая - подтекстом. Теперь представьте 5 из них друг на друга. Как этого добиться? Ах да ... ты не можешь. Хорошо, что ничего такого необычного никогда не требовалось, иначе Google выглядел бы глупо, упустив из виду такую ​​базовую функциональность макета в своем в остальном всеобъемлющем наборе инструментов макета.
Евгений Симкин
3
@ Доктор Дредел вау, хотя я согласен с тем, что вы говорите (использование RadioButtons), но, может быть, ваша реакция была слишком эмоциональной? :)
инфографнет
14
Это было не столько эмоционально, сколько явно раздражало. Что этот комментарий предлагает OP? Что вообще предлагает ветке? Это подразумевает, что вопрос не заслуживает внимания, является нетерпеливым и язвительным. Если бы он начал это со слов «Не могли бы вы объяснить, почему вы хотите это сделать?», Это было бы уместно и вежливо. «Я вынужден спросить» - это тонко завуалированная альтернатива «какому идиоту понадобится этот дурацкий клудж?». По крайней мере, я так это читал.
Евгений Симкин
1
Почему разработчики Android до сих пор не позволяют использовать LinearLayout внутри RadioGroup? Выпущен Marshmallow.
Shan Xeeshi
1
Все еще нет правильного ответа? Я искала решение
neena

Ответы:

49

Кажется, что хорошие люди в Google / Android предполагают, что при использовании RadioButtons вам не нужна гибкость, которая присуща всем остальным аспектам системы пользовательского интерфейса / компоновки Android. Проще говоря: они не хотят, чтобы вы вкладывали макеты и переключатели. Вздох.

Так что вам нужно обойти проблему. Это означает, что вы должны реализовать переключатели самостоятельно.

Это действительно не так уж и сложно. В вашем onCreate () установите свои RadioButtons с их собственным onClick (), чтобы при их активации они устанавливалиChecked (true) и делали противоположное для других кнопок. Например:

class FooActivity {

    RadioButton m_one, m_two, m_three;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        m_one = (RadioButton) findViewById(R.id.first_radio_button);
        m_two = (RadioButton) findViewById(R.id.second_radio_button);
        m_three = (RadioButton) findViewById(R.id.third_radio_button);

        m_one.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                m_one.setChecked(true);
                m_two.setChecked(false);
                m_three.setChecked(false);
            }
        });

        m_two.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                m_one.setChecked(false);
                m_two.setChecked(true);
                m_three.setChecked(false);
            }
        });

        m_three.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                m_one.setChecked(false);
                m_two.setChecked(false);
                m_three.setChecked(true);
            }
        });

        ...     
    } // onCreate() 

}

Да, я знаю - старая школа. Но это работает. Удачи!

SMBiggs
источник
34
бесить. просто невероятно, что это уровень клудеры, необходимый для того, чтобы делать что-то столь обыденное, как "переключатель". Невозможно поверить, что Google дает нам так много сокращений для вещей, которые почти полностью бесполезны (например, 80% виджетов Animations), а затем оставляет нас собирать вместе наши собственные переключатели. (плевать!).
Евгений Симкин
3
@ Доктор Дредел: Да, я согласен с тем, что многие варианты пользовательского интерфейса у них странные. Я могу только догадываться об этом ограничении, что они могут думать: «На самом деле это не так уж сложно сделать вручную». Но было бы неплохо, если бы они хотя бы немного задокументировали это отсутствие функции (например, на странице руководства?). Как вы указываете, они просто переборщили с другими, почти бесполезными вещами (возможно, домашними проектами?).
SMBiggs
3
Я могу только догадываться, но в целом у меня сложилось впечатление, что команда разработчиков пользовательского интерфейса Android либо недооценена, либо в целом довольно слаба. Рассмотрим, что во вселенной Google считается «элегантным». Все это действительно по-спартански и утилитарно. Я не фанат Apple, потому что предпочитаю функциональность стилю, но если когда-либо возникнет мега-компания с полчищами денежных средств, чтобы переосмыслить ее внешний вид (вверх и вниз по цепочке), я не могу придумать лучшего кандидата, чем Google.
Евгений Симкин
1
Это, безусловно, одно из самых надежных и простых решений ... хотя и доисторическое, жаль, что Google не реализовал что-то более эффективное ...
ТВ
3
Да ... Я ожидал, что что-то вроде ручного присвоения идентификаторов переключателей RadioGroup или что-то будет существовать, если будет дорого обходиться автоматический обход дополнительных групп просмотра, которые не содержат переключателей внутри группы переключателей ... Я был так уверен, что что-то вроде это существует, поэтому я начал поиск. Я сейчас в отчаянии покидаю этот пост.
Dreamingwhale,
28

Используйте этот класс, который я создал. Он найдет все проверяемые дочерние элементы в вашей иерархии.

import java.util.ArrayList;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Checkable;
import android.widget.LinearLayout;

public class MyRadioGroup extends LinearLayout {

private ArrayList<View> mCheckables = new ArrayList<View>();

public MyRadioGroup(Context context) {
    super(context);
}

public MyRadioGroup(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public MyRadioGroup(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

@Override
public void addView(View child, int index,
        android.view.ViewGroup.LayoutParams params) {
    super.addView(child, index, params);
    parseChild(child);
}

public void parseChild(final View child)
{
    if(child instanceof Checkable)
    {
        mCheckables.add(child);
        child.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {
                for(int i = 0; i < mCheckables.size();i++)
                {
                    Checkable view = (Checkable) mCheckables.get(i);
                    if(view == v)
                    {
                        ((Checkable)view).setChecked(true);
                    }
                    else
                    {
                        ((Checkable)view).setChecked(false);
                    }
                }
            }
        });
    }
    else if(child instanceof ViewGroup)
    {
        parseChildren((ViewGroup)child);
    }
}

public void parseChildren(final ViewGroup child)
{
    for (int i = 0; i < child.getChildCount();i++)
    {
        parseChild(child.getChildAt(i));
    }
}
}
Lostdev
источник
учитывая этот код, как мне получить текущую выбранную кнопку?
j2emanue 08
я просто вставил переменную mCheckedview, когда вы установили ((Checkable) view) .setChecked (true); и я возвращаю эту переменную, когда мне нужно знать, какая из них была проверена. Кажется, сейчас все в порядке, но мне нужно выполнить "performClick ()" по умолчанию. спасибо
j2emanue 08
17

Я написал этот простой класс.

Просто используйте это так:

// add any number of RadioButton resource IDs here
GRadioGroup gr = new GRadioGroup(this, 
    R.id.radioButton1, R.id.radioButton2, R.id.radioButton3);

или

GRadioGroup gr = new GRadioGroup(rb1, rb2, rb3);
// where RadioButton rb1 = (RadioButton) findViewById(R.id.radioButton1);
// etc.

Вы можете вызвать его, например, в onCreate () of Activity. Независимо от того, на что RadioButtonвы щелкнете, остальные не будут отмечены. Также не имеет значения, RadioButtonsнаходятся ли некоторые из них внутри некоторых RadioGroupили нет.

Вот класс:

package pl.infografnet.GClasses;

import java.util.ArrayList;
import java.util.List;

import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewParent;
import android.widget.RadioButton;
import android.widget.RadioGroup;

public class GRadioGroup {

    List<RadioButton> radios = new ArrayList<RadioButton>();

    /**
     * Constructor, which allows you to pass number of RadioButton instances,
     * making a group.
     * 
     * @param radios
     *            One RadioButton or more.
     */
    public GRadioGroup(RadioButton... radios) {
        super();

        for (RadioButton rb : radios) {
            this.radios.add(rb);
            rb.setOnClickListener(onClick);
        }
    }

    /**
     * Constructor, which allows you to pass number of RadioButtons 
     * represented by resource IDs, making a group.
     * 
     * @param activity
     *            Current View (or Activity) to which those RadioButtons 
     *            belong.
     * @param radiosIDs
     *            One RadioButton or more.
     */
    public GRadioGroup(View activity, int... radiosIDs) {
        super();

        for (int radioButtonID : radiosIDs) {
            RadioButton rb = (RadioButton)activity.findViewById(radioButtonID);
            if (rb != null) {
                this.radios.add(rb);
                rb.setOnClickListener(onClick);
            }
        }
    }

    /**
     * This occurs everytime when one of RadioButtons is clicked, 
     * and deselects all others in the group.
     */
    OnClickListener onClick = new OnClickListener() {

        @Override
        public void onClick(View v) {

            // let's deselect all radios in group
            for (RadioButton rb : radios) {

                ViewParent p = rb.getParent();
                if (p.getClass().equals(RadioGroup.class)) {
                    // if RadioButton belongs to RadioGroup, 
                    // then deselect all radios in it 
                    RadioGroup rg = (RadioGroup) p;
                    rg.clearCheck();
                } else {
                    // if RadioButton DOES NOT belong to RadioGroup, 
                    // just deselect it
                    rb.setChecked(false);
                }
            }

            // now let's select currently clicked RadioButton
            if (v.getClass().equals(RadioButton.class)) {
                RadioButton rb = (RadioButton) v;
                rb.setChecked(true);
            }

        }
    };

}
инфографнет
источник
1
Ницца. Если вы замените RadioButton суперклассом CompoundButton, тогда это станет еще лучше, так как вы сможете добавить в группу любые переключаемые кнопки (например, ToggleButton)!
Neromancer
1
Стоит отметить, что выполнение getCheckedRadioButtonId () из вашей обычной радиогруппы больше не будет работать (всегда возвращает -1), если радиокнопки не вложены напрямую в радиогруппу. Я добавил еще один метод к классу выше: `/ ** * Возвращает идентификатор переключателя, который отмечен, или -1, если ни один не отмечен * @return * / public int getCheckedRadioButtonId () {int checkedId = -1; // Зациклить каждый переключатель для (RadioButton rb: радио) {if (rb.isChecked ()) {return rb.getId (); }} return checkedId; } `
sham
15

Вот мое решение, основанное на решении @lostdev и реализации RadioGroup. Это RadioGroup, модифицированная для работы с RadioButton (или другими CompoundButton), вложенными в дочерние макеты.

import android.content.Context;
import android.os.Build;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.RadioButton;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * This class is a replacement for android RadioGroup - it supports
 * child layouts which standard RadioGroup doesn't.
 */
public class RecursiveRadioGroup extends LinearLayout {

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

    /**
     * For generating unique view IDs on API < 17 with {@link #generateViewId()}.
     */
    private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);

    private CompoundButton checkedView;

    private CompoundButton.OnCheckedChangeListener childOnCheckedChangeListener;

    /**
     * When this flag is true, onCheckedChangeListener discards events.
     */
    private boolean mProtectFromCheckedChange = false;

    private OnCheckedChangeListener onCheckedChangeListener;

    private PassThroughHierarchyChangeListener mPassThroughListener;

    public RecursiveRadioGroup(Context context) {
        super(context);
        setOrientation(HORIZONTAL);
        init();
    }

    public RecursiveRadioGroup(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RecursiveRadioGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        childOnCheckedChangeListener = new CheckedStateTracker();
        mPassThroughListener = new PassThroughHierarchyChangeListener();

        super.setOnHierarchyChangeListener(mPassThroughListener);
    }

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

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        // checks the appropriate radio button as requested in the XML file
        if (checkedView != null) {
            mProtectFromCheckedChange = true;
            setCheckedStateForView(checkedView, true);
            mProtectFromCheckedChange = false;
            setCheckedView(checkedView);
        }
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        parseChild(child);

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

    private void parseChild(final View child) {
        if (child instanceof CompoundButton) {
            final CompoundButton checkable = (CompoundButton) child;

            if (checkable.isChecked()) {
                mProtectFromCheckedChange = true;
                if (checkedView != null) {
                    setCheckedStateForView(checkedView, false);
                }
                mProtectFromCheckedChange = false;
                setCheckedView(checkable);
            }
        } else if (child instanceof ViewGroup) {
            parseChildren((ViewGroup) child);
        }
    }

    private void parseChildren(final ViewGroup child) {
        for (int i = 0; i < child.getChildCount(); i++) {
            parseChild(child.getChildAt(i));
        }
    }

    /**
     * <p>Sets the selection to the radio button whose identifier is passed in
     * parameter. Using -1 as the selection identifier clears the selection;
     * such an operation is equivalent to invoking {@link #clearCheck()}.</p>
     *
     * @param view the radio button to select in this group
     * @see #getCheckedItemId()
     * @see #clearCheck()
     */
    public void check(CompoundButton view) {
        if(checkedView != null) {
            setCheckedStateForView(checkedView, false);
        }

        if(view != null) {
            setCheckedStateForView(view, true);
        }

        setCheckedView(view);
    }

    private void setCheckedView(CompoundButton view) {
        checkedView = view;

        if(onCheckedChangeListener != null) {
            onCheckedChangeListener.onCheckedChanged(this, checkedView.getId());
        }
    }

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

    /**
     * <p>Returns the identifier of the selected radio button in this group.
     * Upon empty selection, the returned value is -1.</p>
     *
     * @return the unique id of the selected radio button in this group
     * @attr ref android.R.styleable#RadioGroup_checkedButton
     * @see #check(CompoundButton)
     * @see #clearCheck()
     */
    @IdRes
    public int getCheckedItemId() {
        return checkedView.getId();
    }

    public CompoundButton getCheckedItem() {
        return checkedView;
    }

    /**
     * <p>Clears the selection. When the selection is cleared, no radio button
     * in this group is selected and {@link #getCheckedItemId()} returns
     * null.</p>
     *
     * @see #check(CompoundButton)
     * @see #getCheckedItemId()
     */
    public void clearCheck() {
        check(null);
    }

    /**
     * <p>Register a callback to be invoked when the checked radio button
     * changes in this group.</p>
     *
     * @param listener the callback to call on checked state change
     */
    public void setOnCheckedChangeListener(RecursiveRadioGroup.OnCheckedChangeListener listener) {
        onCheckedChangeListener = listener;
    }

    /**
     * Generate a value suitable for use in {@link #setId(int)}.
     * This value will not collide with ID values generated at build time by aapt for R.id.
     *
     * @return a generated ID value
     */
    public static int generateViewId() {
        for (; ; ) {
            final int result = sNextGeneratedId.get();
            // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
            int newValue = result + 1;
            if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
            if (sNextGeneratedId.compareAndSet(result, newValue)) {
                return result;
            }
        }
    }

    private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener {

        @Override
        public void onCheckedChanged(CompoundButton view, boolean b) {
            if (mProtectFromCheckedChange) {
                return;
            }

            mProtectFromCheckedChange = true;
            if (checkedView != null) {
                setCheckedStateForView(checkedView, false);
            }
            mProtectFromCheckedChange = false;

            int id = view.getId();
            setCheckedView(view);
        }
    }

    private class PassThroughHierarchyChangeListener implements OnHierarchyChangeListener {

        private OnHierarchyChangeListener mOnHierarchyChangeListener;

        @Override
        public void onChildViewAdded(View parent, View child) {
            if (child instanceof CompoundButton) {
                int id = child.getId();

                if (id == View.NO_ID) {
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
                        child.setId(generateViewId());
                    } else {
                        child.setId(View.generateViewId());
                    }
                }

                ((CompoundButton) child).setOnCheckedChangeListener(childOnCheckedChangeListener);

                if (mOnHierarchyChangeListener != null) {
                    mOnHierarchyChangeListener.onChildViewAdded(parent, child);
                }
            } else if(child instanceof ViewGroup) {
                // View hierarchy seems to be constructed from the bottom up,
                // so all child views are already added. That's why we
                // manually call the listener for all children of ViewGroup.
                for(int i = 0; i < ((ViewGroup) child).getChildCount(); i++) {
                    onChildViewAdded(child, ((ViewGroup) child).getChildAt(i));
                }
            }
        }

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

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

}

Вы можете использовать его в своем макете так же, как и обычный, RadioGroupза исключением того, что он также работает с вложенными RadioButtonпредставлениями:

<RecursiveRadioGroup
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    android:layout_marginBottom="16dp"
    android:layout_marginLeft="16dp"
    android:layout_marginRight="16dp"
    android:orientation="horizontal">

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">

        <RadioButton
            android:id="@+id/rbNotEnoughProfileInfo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Not enough profile information"/>

        <RadioButton
            android:id="@+id/rbNotAGoodFit"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Not a good fit"/>

        <RadioButton
            android:id="@+id/rbDatesNoLongerAvailable"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Dates no longer available"/>

    </LinearLayout>

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="vertical">

        <RadioButton
            android:id="@+id/rbOther"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Other"/>

        <android.support.v7.widget.AppCompatEditText
            android:id="@+id/etReason"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@+id/tvMessageError"
            android:textSize="15sp"
            android:gravity="top|left"
            android:hint="Tell us more"
            android:padding="16dp"
            android:background="@drawable/edit_text_multiline_background"/>
    </LinearLayout>

</RecursiveRadioGroup>
Иван Кушт
источник
6

Это решение не было опубликовано, поэтому размещаем:

Шаг 0: Создайте CompoundButton previousCheckedCompoundButton;глобальную переменную as.

Шаг 1. Создайте OnCheckedChangedListenerпереключатели

CompoundButton.OnCheckedChangeListener onRadioButtonCheckedListener = new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (!isChecked) return;
            if (previousCheckedCompoundButton != null) {
                previousCheckedCompoundButton.setChecked(false);
                previousCheckedCompoundButton = buttonView;
            } else {
                previousCheckedCompoundButton = buttonView;
            }
        }
    };

Шаг 3: добавьте слушателя ко всем переключателям:

radioButton1.setOnCheckedChangeListener(onRadioButtonCheckedListener);
radioButton2.setOnCheckedChangeListener(onRadioButtonCheckedListener);
radioButton3.setOnCheckedChangeListener(onRadioButtonCheckedListener);
radioButton4.setOnCheckedChangeListener(onRadioButtonCheckedListener);

Это оно!! все готово.

Панкадж
источник
5

Вздох .. Действительно виноват, что Android не хватает такой базовой функциональности.

Адаптировано из ответа @ScottBiggs, вот, возможно, самый короткий способ сделать это с помощью Kotlin:

var currentSelected = button1
listOf<RadioButton>(
    button1, button2, button3, ...
).forEach {
    it.setOnClickListener { _ ->
        currentSelected.isChecked = false
        currentSelected = it
        currentSelected.isChecked = true
    }
}
а именно
источник
в вашем ответе нет логики, проверьте его более внимательно
Эдгар Химич
@EdgarKhimich, что значит "нет логики" ..? мой код просто и элегантно отвечает на исходный вопрос о том, как сгруппировать несколько переключателей. мы не устанавливаем никакой другой onclicklistener, кроме простого переключения проверки.
а именно
Это идеально ... работает как шарм и не добавляет много кода. Спасибо!
kwishnu
3

Я создал эти два метода для решения этой проблемы. Все, что вам нужно сделать, это передать ViewGroup, где находятся RadioButtons (может быть RadioGroup, LinearLayout, RelativeLayout и т. Д.), И он устанавливает исключительно события OnClick, то есть всякий раз, когда один из RadioButton является дочерним элементом ViewGroup ( на любом уровне вложенности) выбрано, остальные не выбраны. Он работает с любым количеством вложенных макетов.

public class Utils {
    public static void setRadioExclusiveClick(ViewGroup parent) {
        final List<RadioButton> radios = getRadioButtons(parent);

        for (RadioButton radio: radios) {
            radio.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    RadioButton r = (RadioButton) v;
                    r.setChecked(true);
                    for (RadioButton r2:radios) {
                        if (r2.getId() != r.getId()) {
                            r2.setChecked(false);
                        }
                    }

                }
            });
        }
    }

    private static List<RadioButton> getRadioButtons(ViewGroup parent) {
        List<RadioButton> radios = new ArrayList<RadioButton>();
        for (int i=0;i < parent.getChildCount(); i++) {
            View v = parent.getChildAt(i);
            if (v instanceof RadioButton) {
                radios.add((RadioButton) v);
            } else if (v instanceof ViewGroup) {
                List<RadioButton> nestedRadios = getRadioButtons((ViewGroup) v);
                radios.addAll(nestedRadios);
            }
        }
        return radios;
    }
}

Использование внутри действия будет таким:

ViewGroup parent = findViewById(R.id.radios_parent);
Utils.setRadioExclusiveClick(parent);
Луккас Корреа
источник
2

Я написал свой собственный класс радиогруппы, который позволяет содержать вложенные радиокнопки. Проверить это. Если вы обнаружите ошибки, дайте мне знать.

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.LinearLayout;

/**
 * This class is used to create a multiple-exclusion scope for a set of compound
 * buttons. Checking one compound button that belongs to a group unchecks any
 * previously checked compound button within the same group. Intially, all of
 * the compound buttons are unchecked. While it is not possible to uncheck a
 * particular compound button, the group can be cleared to remove the checked
 * state. Basically, this class extends functionality of
 * {@link android.widget.RadioGroup} because it doesn't require that compound
 * buttons are direct childs of the group. This means you can wrap compound
 * buttons with other views. <br>
 * <br>
 * 
 * <b>IMPORTATNT! Follow these instruction when using this class:</b><br>
 * 1. Each direct child of this group must contain one compound button or be
 * compound button itself.<br>
 * 2. Do not set any "on click" or "on checked changed" listeners for the childs
 * of this group.
 */
public class CompoundButtonsGroup extends LinearLayout {

 private View checkedView;
 private OnCheckedChangeListener listener;
 private OnHierarchyChangeListener onHierarchyChangeListener;

 private OnHierarchyChangeListener onHierarchyChangeListenerInternal = new OnHierarchyChangeListener() {

  @Override
  public final void onChildViewAdded(View parent, View child) {
   notifyHierarchyChanged(null);
   if (CompoundButtonsGroup.this.onHierarchyChangeListener != null) {
    CompoundButtonsGroup.this.onHierarchyChangeListener.onChildViewAdded(
      parent, child);
   }
  }

  @Override
  public final void onChildViewRemoved(View parent, View child) {
   notifyHierarchyChanged(child);
   if (CompoundButtonsGroup.this.onHierarchyChangeListener != null) {
    CompoundButtonsGroup.this.onHierarchyChangeListener.onChildViewRemoved(
      parent, child);
   }
  }
 };

 public CompoundButtonsGroup(Context context) {
  super(context);
  init();
 }

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

 public CompoundButtonsGroup(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  init();
 }

 private void init() {
  super.setOnHierarchyChangeListener(this.onHierarchyChangeListenerInternal);
 }

 @Override
 public final void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
  this.onHierarchyChangeListener = listener;
 }

 /**
  * Register a callback to be invoked when the checked view changes in this
  * group.
  * 
  * @param listener
  *            the callback to call on checked state change.
  */
 public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
  this.listener = listener;
 }

 /**
  * Returns currently selected view in this group. Upon empty selection, the
  * returned value is null.
  */
 public View getCheckedView() {
  return this.checkedView;
 }

 /**
  * Returns index of currently selected view in this group. Upon empty
  * selection, the returned value is -1.
  */
 public int getCheckedViewIndex() {
  return (this.checkedView != null) ? indexOfChild(this.checkedView) : -1;
 }

 /**
  * Sets the selection to the view whose index in group is passed in
  * parameter.
  * 
  * @param index
  *            the index of the view to select in this group.
  */
 public void check(int index) {
  check(getChildAt(index));
 }

 /**
  * Clears the selection. When the selection is cleared, no view in this
  * group is selected and {@link #getCheckedView()} returns null.
  */
 public void clearCheck() {
  if (this.checkedView != null) {
   findCompoundButton(this.checkedView).setChecked(false);
   this.checkedView = null;
   onCheckedChanged();
  }
 }

 private void onCheckedChanged() {
  if (this.listener != null) {
   this.listener.onCheckedChanged(this.checkedView);
  }
 }

 private void check(View child) {
  if (this.checkedView == null || !this.checkedView.equals(child)) {
   if (this.checkedView != null) {
    findCompoundButton(this.checkedView).setChecked(false);
   }

   CompoundButton comBtn = findCompoundButton(child);
   comBtn.setChecked(true);

   this.checkedView = child;
   onCheckedChanged();
  }
 }

 private void notifyHierarchyChanged(View removedView) {
  for (int i = 0; i < getChildCount(); i++) {
   View child = getChildAt(i);
   child.setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View v) {
     check(v);
    }
   });
   CompoundButton comBtn = findCompoundButton(child);
   comBtn.setClickable(comBtn.equals(child));
  }

  if (this.checkedView != null && removedView != null
    && this.checkedView.equals(removedView)) {
   clearCheck();
  }
 }

 private CompoundButton findCompoundButton(View view) {
  if (view instanceof CompoundButton) {
   return (CompoundButton) view;
  }

  if (view instanceof ViewGroup) {
   for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
    CompoundButton compoundBtn = findCompoundButton(((ViewGroup) view)
      .getChildAt(i));
    if (compoundBtn != null) {
     return compoundBtn;
    }
   }
  }

  return null;
 }

 /**
  * Interface definition for a callback to be invoked when the checked view
  * changed in this group.
  */
 public interface OnCheckedChangeListener {

  /**
   * Called when the checked view has changed.
   * 
   * @param checkedView
   *            newly checked view or null if selection was cleared in the
   *            group.
   */
  public void onCheckedChanged(View checkedView);
 }

}
Эгис
источник
2

Вам нужно сделать две вещи:

  1. Использовать mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
  2. Сделайте свое собственное представление строки реализацией Checkable.

Поэтому я думаю, что лучшим решением является реализация Checkable внутри вашего внутреннего LinearLayout: (спасибо daichan4649, по его ссылке https://gist.github.com/daichan4649/5245378 , я взял весь код, вставленный ниже)

CheckableLayout.java

package daichan4649.test;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Checkable;
import android.widget.LinearLayout;

public class CheckableLayout extends LinearLayout implements Checkable {

    private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };

    public CheckableLayout(Context context) {
        super(context, null);
    }

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

    public CheckableLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    private boolean checked;

    @Override
    public boolean isChecked() {
        return checked;
    }

    @Override
    public void setChecked(boolean checked) {
        if (this.checked != checked) {
            this.checked = checked;
            refreshDrawableState();

            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                if (child instanceof Checkable) {
                    ((Checkable) child).setChecked(checked);
                }
            }
        }
    }

    @Override
    public void toggle() {
        setChecked(!checked);
    }

    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked()) {
            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
        }
        return drawableState;
    }
}

inflater_list_column.xml

<?xml version="1.0" encoding="utf-8"?>
<daichan4649.test.CheckableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/check_area"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical">

    <TextView
        android:id="@+id/text"
        android:layout_width="0dip"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:gravity="center_vertical" />

    <RadioButton
        android:id="@+id/radio"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:clickable="false"
        android:focusable="false"
        android:focusableInTouchMode="false" />

</daichan4649.test.CheckableLayout>

TestFragment.java

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.fragment_test, container, false);

    // 表示データ
    List<String> dataList = new ArrayList<String>();

    // 初期選択位置
    int initSelectedPosition = 3;

    // リスト設定
    TestAdapter adapter = new TestAdapter(getActivity(), dataList);
    ListView listView = (ListView) view.findViewById(R.id.list);
    listView.setAdapter(adapter);
    listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    listView.setItemChecked(initSelectedPosition, true);

    listView.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            // 選択状態を要素(checkable)へ反映
            Checkable child = (Checkable) parent.getChildAt(position);
            child.toggle();
        }
    });
    return view;
}

private static class TestAdapter extends ArrayAdapter<String> {

    private LayoutInflater inflater;

    public TestAdapter(Context context, List<String> dataList) {
        super(context, 0, dataList);
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final ViewHolder holder;
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.inflater_list_column, null);
            holder = new ViewHolder();
            holder.text = (TextView) convertView.findViewById(R.id.text);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        // bindData
        holder.text.setText(getItem(position));
        return convertView;
    }
}

private static class ViewHolder {
    TextView text;
}
Madx
источник
2

Я столкнулся с той же проблемой, что и я хочу разместить 4 разных переключателя в двух разных linearlayout, и этот макет будет дочерним элементом группы radio. Чтобы добиться желаемого поведения в RadioGroup, я перегрузил функцию addView.

Вот решение

public class AgentRadioGroup extends RadioGroup
{

    public AgentRadioGroup(Context context) {
        super(context);
    }

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

    @Override
    public void onViewAdded(View child) {
        if( child instanceof ViewGroup)
        {
            ViewGroup viewGroup = (ViewGroup) child;
            for(int i=0; i<viewGroup.getChildCount(); i++)
            {
                View subChild = viewGroup.getChildAt(i);
                if( subChild instanceof ViewGroup )
                {
                    onViewAdded(subChild);
                }
                else
                {
                    if (subChild instanceof RadioButton) {
                        super.onViewAdded(subChild);
                    }
                }
            }
        }
        if (child instanceof RadioButton)
        {
            super.onViewAdded(child);
        }
    }
}
umerk44
источник
1

Ничто не мешает вам реализовать эту структуру макета ( RadioGroupна самом деле является ее подклассом LinearLayout), но вам не следует этого делать. Во-первых, вы создаете структуру на 4 уровня (используя другую структуру макета, которую вы можете оптимизировать), а во-вторых, если вы RadioButtonsне являетесь прямыми дочерними элементами a RadioGroup, единственный элемент, выбранный в группе , не будет работать. Это означает, что если вы выберете один Radiobuttonиз этого макета, а затем выберете другой, RadioButtonвы получите два RadioButtonsвыбранных вместо последнего выбранного.

Если вы объясните, что вы хотите сделать в этом макете, возможно, я могу порекомендовать вам альтернативу.

пользователь
источник
Luksprog, спасибо за объяснение. Если я правильно понимаю, если RadioButtons не являются прямыми дочерними элементами радиогруппы, это не сработает.
marcoqf73
1
@ marcoqf73 Да, поставить его более простым, если у вас есть что - нибудь в макете между RadioButtonsи родителем , RadioGroupто это не будет работать , как обычно , и в основном вы будете в конечном итоге с LinearLayoutзаполнены RadioButtons.
пользователь
2
Есть масса причин сделать что-то подобное. Например, вы можете захотеть иметь больший контроль над своими макетами, чем простой LinearLayout; в моем случае я хочу создать несколько строк RadioButton. Вложенные макеты - это то, как работает ЛЮБОЙ макет Android. Ба, меня тошнит от слов «Вы не можете этого сделать», когда я ищу решения этих причуд пользовательского интерфейса, о которых я слышу через день. :(
SMBiggs 07
@ScottBiggs Я не говорил, что вы не можете этого сделать, я сказал, что попытка того, что задает пользователь, не сработает. Вы можете реализовать свой собственный макет (но сделать это не так просто) или использовать трюк, как в этом ответе моего stackoverflow.com/questions/10425569/… .
пользователь
Я создал класс радиогруппы, который расширил макет таблицы и добавил функции из класса радиогруппы. Он довольно хорошо работает с неограниченным количеством столбцов, динамически добавляя переключатели. stackoverflow.com/questions/10425569/…
Кристи Уэлш
1

Мои 0,02 доллара на основе @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!
    }
});

Бонус: вы можете получить последнюю проверенную кнопку, список всех кнопок, и вы можете проверить любую кнопку по индексу!

Ник
источник
отличное решение! это работает для меня. Только вам нужно назначить линейные макеты внутри нового слушателя onClick, потому что только если вы коснетесь круга радиокнопки, выбор изменится.
benoffi7
1
    int currentCheckedRadioButton = 0;
    int[] myRadioButtons= new int[6];
    myRadioButtons[0] = R.id.first;
    myRadioButtons[1] = R.id.second;
    //..
    for (int radioButtonID : myRadioButtons) {
        findViewById(radioButtonID).setOnClickListener(
                    new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (currentCheckedRadioButton != 0)
                    ((RadioButton) findViewById(currentCheckedRadioButton)).setChecked(false);
                currentCheckedRadioButton = v.getId();

            }
        });
    }
med.Hamdan
источник
0

Хотя это, возможно, более старая тема, я хотел бы быстро поделиться простым хакерским кодом, который я написал .. Это не для всех, и его тоже можно немного улучшить ..

Ситуация использовать этот код ??
Этот код предназначен для людей, у которых есть макет исходного вопроса или аналогичный, в моем случае это было так, как показано ниже. Это лично было для диалога, который я использовал.

  • LinLayout_Main
    • LinLayout_Row1
      • ImageView
      • Переключатель
    • LinLayout_Row2
      • ImageView
      • Переключатель
    • LinLayout_Row3
      • ImageView
      • Переключатель

Что делает сам код ??
Этот код будет перечислять все дочерние элементы «LinLayout_Main», а затем для каждого дочернего элемента, являющегося «LinearLayout», он будет перечислять этот View для любых RadioButton.

Просто он будет искать родительский «LinLayout_Main» и находить любые RadioButton, которые есть в любом дочернем LinearLayouts.

MyMethod_ShowDialog
покажет диалоговое окно с файлом макета XML, а также будет искать его, чтобы установить "setOnClickListener" для каждого найденного RadioButton.

MyMethod_ClickRadio Зацикливает
каждый RadioButton так же, как и MyMethod_ShowDialog, но вместо установки «setOnClickListener» он вместо этого будет «setChecked (false)», чтобы очистить каждый RadioButton, а затем, когда последний шаг будет «setChecked (false)» для RadioButton, который называется событием клика.

public void MyMethod_ShowDialog(final double tmpLat, final double tmpLng) {
        final Dialog dialog = new Dialog(actMain);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(R.layout.layout_dialogXML);

        final LinearLayout tmpLayMain = (LinearLayout)dialog.findViewById(R.id.LinLayout_Main);
        if (tmpLayMain!=null) {
            // Perform look for each child of main LinearLayout
            int iChildCount1 = tmpLayMain.getChildCount();
            for (int iLoop1=0; iLoop1 < iChildCount1; iLoop1++){
                View tmpChild1 = tmpLayMain.getChildAt(iLoop1);
                if (tmpChild1 instanceof LinearLayout) {
                    // Perform look for each LinearLayout child of main LinearLayout
                    int iChildCount2 = ((LinearLayout) tmpChild1).getChildCount();
                    for (int iLoop2=0; iLoop2 < iChildCount2; iLoop2++){
                        View tmpChild2 = ((LinearLayout) tmpChild1).getChildAt(iLoop2);
                        if (tmpChild2 instanceof RadioButton) {
                            ((RadioButton) tmpChild2).setOnClickListener(new RadioButton.OnClickListener() {
                                public void onClick(View v) {
                                    MyMethod_ClickRadio(v, dialog);
                                }
                            });
                        }
                    }
                }
            }

            Button dialogButton = (Button)dialog.findViewById(R.id.LinLayout_Save);
            dialogButton.setOnClickListener(new Button.OnClickListener() {
                public void onClick(View v) {
                    dialog.dismiss();
                }
            });
        }
       dialog.show();
}


public void MyMethod_ClickRadio(View vRadio, final Dialog dDialog) {

        final LinearLayout tmpLayMain = (LinearLayout)dDialog.findViewById(R.id.LinLayout_Main);
        if (tmpLayMain!=null) {
            int iChildCount1 = tmpLayMain.getChildCount();
            for (int iLoop1=0; iLoop1 < iChildCount1; iLoop1++){
                View tmpChild1 = tmpLayMain.getChildAt(iLoop1);
                if (tmpChild1 instanceof LinearLayout) {
                    int iChildCount2 = ((LinearLayout) tmpChild1).getChildCount();
                    for (int iLoop2=0; iLoop2 < iChildCount2; iLoop2++){
                        View tmpChild2 = ((LinearLayout) tmpChild1).getChildAt(iLoop2);
                        if (tmpChild2 instanceof RadioButton) {
                            ((RadioButton) tmpChild2).setChecked(false);
                        }
                    }
                }
            }
        }

        ((RadioButton) vRadio).setChecked(true);
}

Возможны ошибки, скопированные из проекта и переименованные в Voids / XML / ID

Вы также можете запустить такой же цикл, чтобы узнать, какие элементы отмечены

Злой 84
источник
Удалось ли вам заставить это работать. Я пытаюсь создать радиогруппу с сублинейными компоновками, у которых есть переключатель рядом с обычной кнопкой. Я не смог заставить его работать и опубликовал , но мне сказали, что радиогруппа выйдет из строя на любых детях, не являющихся радиокнопками.
abalter
0

Это модифицированная версия решения @ Infografnet. Это просто и удобно.

RadioGroupHelper group = new RadioGroupHelper(this,R.id.radioButton1,R.id.radioButton2); group.radioButtons.get(0).performClick(); //programmatically

Просто скопируйте и вставьте

package com.qamar4p.farmer.ui.custom;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.RadioButton;

public class RadioGroupHelper {

    public List<CompoundButton> radioButtons = new ArrayList<>();

    public RadioGroupHelper(RadioButton... radios) {
        super();
        for (RadioButton rb : radios) {
            add(rb);
        }
    }

    public RadioGroupHelper(Activity activity, int... radiosIDs) {
        this(activity.findViewById(android.R.id.content),radiosIDs);
    }

    public RadioGroupHelper(View rootView, int... radiosIDs) {
        super();
        for (int radioButtonID : radiosIDs) {
            add((RadioButton)rootView.findViewById(radioButtonID));
        }
    }

    private void add(CompoundButton button){
        this.radioButtons.add(button);
        button.setOnClickListener(onClickListener);
    }

    View.OnClickListener onClickListener = v -> {
        for (CompoundButton rb : radioButtons) {
            if(rb != v) rb.setChecked(false);
        }
    };
}
Qamar4P
источник
0

Как показано в ответах, решение - это простой пользовательский взлом. Вот моя минималистичная версия на Котлине.

import android.widget.RadioButton

class SimpleRadioGroup(private val radioButtons: List<RadioButton>) {

    init {
        radioButtons.forEach {
            it.setOnClickListener { clickedButton ->
                radioButtons.forEach { it.isChecked = false }
                (clickedButton as RadioButton).isChecked = true
            }
        }
    }

    val checkedButton: RadioButton?
        get() = radioButtons.firstOrNull { it.isChecked }
}

тогда вам просто нужно сделать что-то подобное в onCreate вашей активности или onViewCreated фрагмента:

SimpleRadioGroup(listOf(radio_button_1, radio_button_2, radio_button_3))
Ахраф Амил
источник
0

Это мое решение на Kotlin для нестандартного макета с RadioButton внутри.

tipInfoContainerFirst.radioButton.isChecked = true

var prevSelected = tipInfoContainerFirst.radioButton
prevSelected.isSelected = true

listOf<RadioButton>(
    tipInfoContainerFirst.radioButton,
    tipInfoContainerSecond.radioButton,
    tipInfoContainerThird.radioButton,
    tipInfoContainerForth.radioButton,
    tipInfoContainerCustom.radioButton
).forEach {
    it.setOnClickListener { _it ->
    if(!it.isSelected) {
        prevSelected.isChecked = false
        prevSelected.isSelected = false
        it.radioButton.isSelected = true
        prevSelected = it.radioButton
    }
  }
}
Эдгар Химич
источник
0

У меня та же проблема, я должен использовать переключатель для пола, и все они были с изображением и текстом, поэтому я попытался решить ее следующим образом.

xml файл:

<RadioGroup
       android:layout_marginTop="40dp"
       android:layout_marginEnd="23dp"
       android:id="@+id/rgGender"
       android:layout_width="match_parent"
       android:layout_below="@id/tvCustomer"
       android:orientation="horizontal"
       android:layout_height="wrap_content">

       <LinearLayout
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:orientation="vertical"
           android:gravity="center_horizontal"
           android:layout_weight="1">
       <RadioButton
           android:id="@+id/rbMale"
           android:layout_width="80dp"
           android:layout_height="60dp"
           android:background="@drawable/male_radio_btn_selector"
           android:button="@null"
           style="@style/RadioButton.Roboto.20sp"/>

           <TextView
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="Male"
               style="@style/TextView.RobotoLight.TxtGrey.18sp"
               android:layout_margin="0dp"
               android:textSize="@dimen/txtsize_20sp"/>
       </LinearLayout>
       <LinearLayout
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:orientation="vertical"
           android:gravity="center_horizontal"
           android:layout_weight="1">
       <RadioButton
           android:layout_weight="1"
           android:gravity="center"
           android:id="@+id/rbFemale"
           android:layout_width="80dp"
           android:layout_height="60dp"
           android:button="@null"
           android:background="@drawable/female_radio_btn_selector"
           style="@style/RadioButton.Roboto.20sp"
           android:textColor="@color/light_grey"/>
           <TextView
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="Female"
               android:layout_margin="0dp"
               style="@style/TextView.RobotoLight.TxtGrey.18sp"
               android:textSize="@dimen/txtsize_20sp"/>
       </LinearLayout>
       <LinearLayout
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:orientation="vertical"
           android:gravity="center_horizontal"
           android:layout_weight="1">
       <RadioButton
           android:layout_weight="1"
           android:gravity="center"
           android:id="@+id/rbOthers"
           android:layout_width="80dp"
           android:layout_height="60dp"
           android:button="@null"
           android:background="@drawable/other_gender_radio_btn_selector"
           style="@style/RadioButton.Roboto.20sp"/>
          <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Other"
              android:layout_margin="0dp"
              style="@style/TextView.RobotoLight.TxtGrey.18sp"
              android:textSize="@dimen/txtsize_20sp"/>
      </LinearLayout>
   </RadioGroup>

В файле java: я установил setOnCheckedChangeListener на все 3 переключателя и метод переопределения, как указано ниже, и он отлично работает для меня.

@Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
   switch (compoundButton.getId()){
       case R.id.rbMale:
           if(rbMale.isChecked()){
               rbMale.setChecked(true);
               rbFemale.setChecked(false);
               rbOther.setChecked(false);
           }
           break;
       case R.id.rbFemale:
           if(rbFemale.isChecked()){
               rbMale.setChecked(false);
               rbFemale.setChecked(true);
               rbOther.setChecked(false);
           }
           break;
       case R.id.rbOthers:
           if(rbOther.isChecked()){
               rbMale.setChecked(false);
               rbFemale.setChecked(false);
               rbOther.setChecked(true);
           }
           break;

   }
    }
Ануприя
источник
0

MixedCompoundButtonGroup сделает это за вас!

MixedCompoundButtonGroup сущность

fun setAll() {
    for (i in 0 until childCount) {
        val child = getChildAt(i)
        setCompoundButtonListener(child)
    }
}  


private fun setCompoundButtonListener(view: View?) {
    if (view == null) return
    if (view is CompoundButton) {
        view.setOnCheckedChangeListener(compoundButtonCheckedChangedListener)
    } else if (view is ViewGroup && view !is RadioGroup) { // NOT RadioGroup!
        for (i in 0 until view.childCount) {
            setCompoundButtonListener(view.getChildAt(i))
        }
    }
}

private fun initCompoundButtonListener() {
    compoundButtonCheckedChangedListener = CompoundButton.OnCheckedChangeListener { compoundButton, isChecked ->
        setChecked(compoundButton, isChecked)
    }
}

private fun setChecked(compoundButton: CompoundButton, isChecked: Boolean) {
    if (isChecked.not()) return
    if (currentCompoundButton != null) {
        currentCompoundButton!!.isChecked = false
        currentCompoundButton = compoundButton
    } else {
        currentCompoundButton = compoundButton
    }
    checkedChangedListener?.onCheckedChanged(currentCompoundButton!!)
}
Avisper
источник
0

Вы можете использовать этот простой код расширения RadioGroup. Отбросьте в него макеты / представления / изображения вместе с RadioButtons, и он будет работать.

Он содержит обратный вызов выбора, который возвращает выбранный RadioButton с его индексом, и вы можете программно установить выбор по индексу или идентификатору:

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RadioButton;
import android.widget.RadioGroup;

import java.util.ArrayList;

public class EnhancedRadioGroup extends RadioGroup implements View.OnClickListener {

    public interface OnSelectionChangedListener {
        void onSelectionChanged(RadioButton radioButton, int index);
    }

    private OnSelectionChangedListener selectionChangedListener;
    ArrayList<RadioButton> radioButtons = new ArrayList<>();

    public EnhancedRadioGroup(Context context) {
        super(context);
    }

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

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed) {
            getRadioButtons();
        }
    }

    private void getRadioButtons() {
        radioButtons.clear();
        checkForRadioButtons(this);
    }

    private void checkForRadioButtons(ViewGroup viewGroup) {
        if (viewGroup == null) {
            return;
        }
        for (int i = 0; i < viewGroup.getChildCount(); i++) {
            View v = viewGroup.getChildAt(i);
            if (v instanceof RadioButton) {
                v.setOnClickListener(this);
                // store index of item
                v.setTag(radioButtons.size());
                radioButtons.add((RadioButton) v);
            }
            else if (v instanceof ViewGroup) {
                checkForRadioButtons((ViewGroup)v);
            }
        }
    }

    public RadioButton getSelectedItem() {
        if (radioButtons.isEmpty()) {
            getRadioButtons();
        }
        for (RadioButton radioButton : radioButtons) {
            if (radioButton.isChecked()) {
                return radioButton;
            }
        }
        return null;
    }

    public void setOnSelectionChanged(OnSelectionChangedListener selectionChangedListener) {
        this.selectionChangedListener = selectionChangedListener;
    }

    public void setSelectedById(int id) {
        if (radioButtons.isEmpty()) {
            getRadioButtons();
        }
        for (RadioButton radioButton : radioButtons) {
            boolean isSelectedRadioButton = radioButton.getId() == id;
            radioButton.setChecked(isSelectedRadioButton);
            if (isSelectedRadioButton && selectionChangedListener != null) {
                selectionChangedListener.onSelectionChanged(radioButton, (int)radioButton.getTag());
            }
        }
    }

    public void setSelectedByIndex(int index) {
        if (radioButtons.isEmpty()) {
            getRadioButtons();
        }
        if (radioButtons.size() > index) {
            setSelectedRadioButton(radioButtons.get(index));
        }
    }

    @Override
    public void onClick(View v) {
        setSelectedRadioButton((RadioButton) v);
    }

    private void setSelectedRadioButton(RadioButton rb) {
        if (radioButtons.isEmpty()) {
            getRadioButtons();
        }
        for (RadioButton radioButton : radioButtons) {
            radioButton.setChecked(rb == radioButton);
        }
        if (selectionChangedListener != null) {
            selectionChangedListener.onSelectionChanged(rb, (int)rb.getTag());
        }
    }
}

Используйте его в макете xml:

    <path.to.your.package.EnhancedRadioGroup>
       Layouts containing RadioButtons/Images/Views and other RadioButtons
    </path.to.your.package.EnhancedRadioGroup>

Чтобы зарегистрироваться на обратный звонок:

        enhancedRadioGroupInstance.setOnSelectionChanged(new EnhancedRadioGroup.OnSelectionChangedListener() {
            @Override
            public void onSelectionChanged(RadioButton radioButton, int index) {

            }
        });
RoyBS
источник