Мне было интересно, можно ли сгруппировать каждый сингл RadioButton
в уникальную, RadioGroup
сохраняя ту же структуру. Моя структура выглядит так:
- LinearLayout_main
- LinearLayout_1
- RadioButton1
- LinearLayout_2
- RadioButton2
- LinearLayout_3
- RadioButton3
- LinearLayout_1
Как видите, теперь все RadioButton
дети разные LinearLayout
. Я пробовал использовать приведенную ниже структуру, но она не работает:
- Радиогруппа
- LinearLayout_main
- LinearLayout_1
- RadioButton1
- LinearLayout_2
- RadioButton2
- LinearLayout_3
- RadioButton3
- LinearLayout_1
- LinearLayout_main
Ответы:
Кажется, что хорошие люди в 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() }
Да, я знаю - старая школа. Но это работает. Удачи!
источник
Используйте этот класс, который я создал. Он найдет все проверяемые дочерние элементы в вашей иерархии.
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)); } } }
источник
Я написал этот простой класс.
Просто используйте это так:
// 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); } } }; }
источник
Вот мое решение, основанное на решении @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>
источник
Это решение не было опубликовано, поэтому размещаем:
Шаг 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);
Это оно!! все готово.
источник
Вздох .. Действительно виноват, что Android не хватает такой базовой функциональности.
Адаптировано из ответа @ScottBiggs, вот, возможно, самый короткий способ сделать это с помощью Kotlin:
var currentSelected = button1 listOf<RadioButton>( button1, button2, button3, ... ).forEach { it.setOnClickListener { _ -> currentSelected.isChecked = false currentSelected = it currentSelected.isChecked = true } }
источник
Я создал эти два метода для решения этой проблемы. Все, что вам нужно сделать, это передать 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);
источник
Я написал свой собственный класс радиогруппы, который позволяет содержать вложенные радиокнопки. Проверить это. Если вы обнаружите ошибки, дайте мне знать.
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); } }
источник
Вам нужно сделать две вещи:
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
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; }
источник
Я столкнулся с той же проблемой, что и я хочу разместить 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); } } }
источник
Ничто не мешает вам реализовать эту структуру макета (
RadioGroup
на самом деле является ее подклассомLinearLayout
), но вам не следует этого делать. Во-первых, вы создаете структуру на 4 уровня (используя другую структуру макета, которую вы можете оптимизировать), а во-вторых, если выRadioButtons
не являетесь прямыми дочерними элементами aRadioGroup
, единственный элемент, выбранный в группе , не будет работать. Это означает, что если вы выберете одинRadiobutton
из этого макета, а затем выберете другой,RadioButton
вы получите дваRadioButtons
выбранных вместо последнего выбранного.Если вы объясните, что вы хотите сделать в этом макете, возможно, я могу порекомендовать вам альтернативу.
источник
RadioButtons
и родителем ,RadioGroup
то это не будет работать , как обычно , и в основном вы будете в конечном итоге сLinearLayout
заполненыRadioButtons
.Мои 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! } });
Бонус: вы можете получить последнюю проверенную кнопку, список всех кнопок, и вы можете проверить любую кнопку по индексу!
источник
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(); } }); }
источник
Хотя это, возможно, более старая тема, я хотел бы быстро поделиться простым хакерским кодом, который я написал .. Это не для всех, и его тоже можно немного улучшить ..
Ситуация использовать этот код ??
Этот код предназначен для людей, у которых есть макет исходного вопроса или аналогичный, в моем случае это было так, как показано ниже. Это лично было для диалога, который я использовал.
Что делает сам код ??
Этот код будет перечислять все дочерние элементы «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
Вы также можете запустить такой же цикл, чтобы узнать, какие элементы отмечены
источник
Это модифицированная версия решения @ 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); } }; }
источник
Как показано в ответах, решение - это простой пользовательский взлом. Вот моя минималистичная версия на Котлине.
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))
источник
Это мое решение на 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 } } }
источник
У меня та же проблема, я должен использовать переключатель для пола, и все они были с изображением и текстом, поэтому я попытался решить ее следующим образом.
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; } }
источник
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!!) }
источник
Вы можете использовать этот простой код расширения 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) { } });
источник