Zastanawiałem się, czy można zgrupować każdy singiel RadioButton
w unikalny sposób, RadioGroup
zachowując tę samą strukturę. Moja konstrukcja wygląda tak:
- LinearLayout_main
- LinearLayout_1
- RadioButton1
- LinearLayout_2
- RadioButton2
- LinearLayout_3
- RadioButton3
- LinearLayout_1
Jak widać, teraz każdy RadioButton
jest dzieckiem innym LinearLayout
. Próbowałem użyć poniższej struktury, ale nie działa:
- Radiogroup
- LinearLayout_main
- LinearLayout_1
- RadioButton1
- LinearLayout_2
- RadioButton2
- LinearLayout_3
- RadioButton3
- LinearLayout_1
- LinearLayout_main
android
radio-button
android-linearlayout
radio-group
marcoqf73
źródło
źródło
Odpowiedzi:
Wygląda na to, że dobrzy ludzie w Google / Androidzie zakładają, że kiedy używasz RadioButtons, nie potrzebujesz elastyczności, która jest oferowana w każdym innym aspekcie systemu interfejsu / układu Androida. Mówiąc prościej: nie chcą, abyś zagnieżdżał układy i przyciski opcji. Westchnienie.
Więc musisz obejść ten problem. Oznacza to, że musisz samodzielnie zaimplementować przyciski opcji.
To naprawdę nie jest zbyt trudne. W swojej onCreate () ustaw swoje RadioButtons z ich własnym onClick (), aby po ich aktywacji ustawiały oneChecked (true) i robiły odwrotnie dla pozostałych przycisków. Na przykład:
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() }
Tak, wiem - stara szkoła. Ale to działa. Powodzenia!
źródło
Użyj tej klasy, którą stworzyłem. Znajdzie wszystkie możliwe do sprawdzenia dzieci w Twojej hierarchii.
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)); } } }
źródło
Cóż, napisałem tę prostą lekcję.
Po prostu użyj tego w ten sposób:
// add any number of RadioButton resource IDs here GRadioGroup gr = new GRadioGroup(this, R.id.radioButton1, R.id.radioButton2, R.id.radioButton3);
lub
GRadioGroup gr = new GRadioGroup(rb1, rb2, rb3); // where RadioButton rb1 = (RadioButton) findViewById(R.id.radioButton1); // etc.
Możesz na przykład wywołać to w onCreate () z Activity. Bez względu na to, które
RadioButton
klikniesz, pozostałe zostaną odznaczone. Nie ma też znaczenia, czy niektórzyRadioButtons
są w środkuRadioGroup
, czy nie.Oto klasa:
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); } } }; }
źródło
Oto moje rozwiązanie oparte na rozwiązaniu @lostdev i implementacji
RadioGroup
. Jest to RadioGroup zmodyfikowane do pracy z RadioButtons (lub innymi CompoundButtons), które są zagnieżdżone w układach podrzędnych.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); } } } }
Możesz go używać w swoim układzie w taki sam sposób, jak zwykłego,
RadioGroup
z wyjątkiem tego, że działa również zRadioButton
widokami zagnieżdżonymi :<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>
źródło
To rozwiązanie nie zostało opublikowane, więc publikowanie:
Krok 0: Utwórz
CompoundButton previousCheckedCompoundButton;
zmienną globalną jako.Krok 1: Utwórz
OnCheckedChangedListener
dla przycisków opcjiCompoundButton.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; } } };
Krok 3: dodaj słuchacza do wszystkich przycisków radiowych:
radioButton1.setOnCheckedChangeListener(onRadioButtonCheckedListener); radioButton2.setOnCheckedChangeListener(onRadioButtonCheckedListener); radioButton3.setOnCheckedChangeListener(onRadioButtonCheckedListener); radioButton4.setOnCheckedChangeListener(onRadioButtonCheckedListener);
Otóż to!! Jesteś skończony.
źródło
Ech ... Naprawdę winę za to, że Androidowi brakuje tak podstawowej funkcjonalności.
Na podstawie odpowiedzi @ScottBiggs, oto możliwie najkrótszy sposób na zrobienie tego z Kotlin:
var currentSelected = button1 listOf<RadioButton>( button1, button2, button3, ... ).forEach { it.setOnClickListener { _ -> currentSelected.isChecked = false currentSelected = it currentSelected.isChecked = true } }
źródło
Stworzyłem te dwie metody, aby rozwiązać ten problem. Wszystko, co musisz zrobić, to przekazać ViewGroup, w którym znajdują się RadioButtons (może to być RadioGroup, LinearLayout, RelativeLayout itp.) I ustawia wyłącznie zdarzenia OnClick, to znaczy zawsze, gdy jeden z RadioButtons jest elementem podrzędnym ViewGroup ( na dowolnym zagnieżdżonym poziomie), pozostałe są odznaczone. Działa z dowolną liczbą zagnieżdżonych układów.
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; } }
Sposób użycia w ramach działania wyglądałby tak:
ViewGroup parent = findViewById(R.id.radios_parent); Utils.setRadioExclusiveClick(parent);
źródło
Napisałem własną klasę grup radiowych, która pozwala zawierać zagnieżdżone przyciski opcji. Sprawdź to. Jeśli znajdziesz błędy, daj mi znać.
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); } }
źródło
Musisz zrobić dwie rzeczy:
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
Checkable
.Myślę więc, że lepszym rozwiązaniem jest zaimplementowanie Checkable wewnątrz twojego wewnętrznego LinearLayout: (dzięki daichan4649, z jego linku https://gist.github.com/daichan4649/5245378 , wziąłem cały kod wklejony poniżej)
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; }
źródło
Stoję przed tym samym problemem, ponieważ chcę umieścić 4 różne przyciski radiowe w dwóch różnych układach liniowych, a ten układ będzie elementem podrzędnym grupy radiowej. Aby osiągnąć pożądane zachowanie w RadioGroup, przeładowałem funkcję addView
Oto rozwiązanie
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); } } }
źródło
Nic nie stoi na przeszkodzie, aby wdrożyć tę strukturę układu (
RadioGroup
w rzeczywistości jest to podklasaLinearLayout
), ale nie powinieneś. Po pierwsze, tworzysz strukturę o głębokości 4 poziomów (używając innej struktury układu możesz to zoptymalizować), a po drugie, jeśliRadioButtons
nie jesteś bezpośrednim dzieckiem aRadioGroup
, jedyny element wybrany w grupie nie będzie działał. Oznacza to, że jeśli wybierzesz jedenRadiobutton
z tego układu, a następnie wybierzesz innyRadioButton
, skończysz z dwomaRadioButtons
wybranymi zamiast ostatnio wybranego.Jeśli wyjaśnisz, co chcesz zrobić w tym układzie, może polecę ci alternatywę.
źródło
RadioButtons
a rodzicem,RadioGroup
to nie będzie działać jak zwykle iw zasadzie skończysz zLinearLayout
wypełnieniemRadioButtons
.Moje 0,02 $ na podstawie @infografnet i @lostdev (również dziękuję @Neromancer za sugestię przycisku złożonego!)
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; } }
Użycie (z dołączonym odbiornikiem):
AdvRadioGroup group = new AdvRadioGroup(findViewById(R.id.YOUR_VIEW)); group.setOnButtonCheckedListener(new AdvRadioGroup.OnButtonCheckedListener() { @Override public void onButtonChecked(CompoundButton button) { // do fun stuff here! } });
Bonus: Możesz uzyskać ostatnio zaznaczony przycisk, listę całych przycisków, a także możesz sprawdzić dowolny przycisk po indeksie!
źródło
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(); } }); }
źródło
Chociaż jest to może starszy temat, chciałbym szybko podzielić się prostym, hakerskim kodem, który napisałem. Nie jest dla wszystkich i przydałoby się też pewne udoskonalenie.
Sytuacja, aby użyć tego kodu?
Ten kod jest dla osób, które mają układ oryginalnego pytania lub podobny, w moim przypadku był jak poniżej. To osobiście dotyczyło Dialogu, którego używałem.
Co kod robi sam?
Ten kod wyliczy kiedykolwiek element podrzędny „LinLayout_Main”, a dla każdego elementu podrzędnego, który jest elementem „LinearLayout”, wyliczy ten widok dla wszystkich elementów RadioButtons.
Po prostu będzie wyglądać na rodzica „LinLayout_Main” i znajdzie wszystkie RadioButtons, które są w dowolnym podrzędnym układzie liniowym.
MyMethod_ShowDialog
okno dialogowe z plikiem układu XML, jednocześnie sprawdzając, czy ustawia "setOnClickListener" dla każdego znalezionego RadioButton
MyMethod_ClickRadio zapętli
każdy element RadioButton w taki sam sposób, jak „MyMethod_ShowDialog”, ale zamiast ustawiać „setOnClickListener”, zamiast tego „setChecked (false)” wyczyści każdy element RadioButton, a w ostatnim kroku „setChecked (false)” do elementu RadioButton, który nazywana zdarzeniem kliknięcia.
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); }
Mogą być błędy skopiowane z projektu i przemianowane na Voids / XML / ID
Możesz także uruchomić ten sam typ pętli, aby dowiedzieć się, które elementy są zaznaczone
źródło
To jest zmodyfikowana wersja rozwiązania @ Infografnet. Jest prosty i łatwy w użyciu.
RadioGroupHelper group = new RadioGroupHelper(this,R.id.radioButton1,R.id.radioButton2); group.radioButtons.get(0).performClick(); //programmatically
Po prostu skopiuj i wklej
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); } }; }
źródło
Jak pokazano w odpowiedziach, rozwiązaniem jest prosty niestandardowy hack. Oto moja minimalistyczna wersja w Kotlinie.
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 } }
wtedy po prostu musisz zrobić coś takiego w onCreate swojego działania lub onViewCreated fragmentu:
SimpleRadioGroup(listOf(radio_button_1, radio_button_2, radio_button_3))
źródło
To jest moje rozwiązanie w Kotlin dla niestandardowego układu z RadioButton w środku.
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 } } }
źródło
Mam ten sam problem, muszę użyć przycisku radiowego dla płci i wszystkie były ze zdjęciem i tekstem, więc próbowałem go rozwiązać w następujący sposób.
plik 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>
W pliku java: Ustawiłem setOnCheckedChangeListener na wszystkich 3 przyciskach opcji i metodę nadpisywania, jak wspomniano poniżej, i działa dobrze dla mnie.
@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; } }
źródło
MixedCompoundButtonGroup zrobi to za Ciebie!
Treść 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!!) }
źródło
Możesz użyć tego prostego kodu rozszerzenia RadioGroup. Upuść w nim dowolne układy / widoki / obrazy wraz z RadioButtons i zadziała.
Zawiera wywołanie zwrotne wyboru, które zwraca wybrany RadioButton z jego indeksem i można ustawić wybór programowo według indeksu lub identyfikatora:
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()); } } }
Użyj go w swoim układzie XML:
<path.to.your.package.EnhancedRadioGroup> Layouts containing RadioButtons/Images/Views and other RadioButtons </path.to.your.package.EnhancedRadioGroup>
Aby zarejestrować się do oddzwonienia:
enhancedRadioGroupInstance.setOnSelectionChanged(new EnhancedRadioGroup.OnSelectionChangedListener() { @Override public void onSelectionChanged(RadioButton radioButton, int index) { } });
źródło