Android 如何从不同的 LinearLayouts 对 RadioButton 进行分组?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10461005/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to group RadioButton from different LinearLayouts?
提问by marcoqf73
I was wondering if is possible to group each single RadioButton
in a unique RadioGroup
maintaining the same structure. My structure look like this:
我想知道是否可以将每个单曲组合RadioButton
成一个独特的RadioGroup
保持相同的结构。我的结构是这样的:
- LinearLayout_main
- LinearLayout_1
- RadioButton1
- LinearLayout_2
- RadioButton2
- LinearLayout_3
- RadioButton3
- LinearLayout_1
- LinearLayout_main
- 线性布局_1
- 单选按钮1
- 线性布局_2
- 单选按钮2
- 线性布局_3
- 单选按钮3
- 线性布局_1
As you can see, now each RadioButton
is a child of different LinearLayout
. I tried using the structure below, but it doesn't work:
如您所见,现在每个人RadioButton
都是不同LinearLayout
. 我尝试使用下面的结构,但它不起作用:
- Radiogroup
- LinearLayout_main
- LinearLayout_1
- RadioButton1
- LinearLayout_2
- RadioButton2
- LinearLayout_3
- RadioButton3
- LinearLayout_1
- LinearLayout_main
- 无线电组
- LinearLayout_main
- 线性布局_1
- 单选按钮1
- 线性布局_2
- 单选按钮2
- 线性布局_3
- 单选按钮3
- 线性布局_1
- LinearLayout_main
回答by Scott Biggs
It seems that the good people at Google/Android assume that when you use RadioButtons, you don't need the flexibility that comes with every other aspect of the Android UI/layout system. To put it simply: they don't want you to nest layouts and radio buttons. Sigh.
似乎 Google/Android 的好人认为当您使用 RadioButtons 时,您不需要 Android UI/布局系统的所有其他方面带来的灵活性。简单地说:他们不希望您嵌套布局和单选按钮。叹。
So you gotta work around the problem. That means you must implement radio buttons on your own.
所以你必须解决这个问题。这意味着您必须自己实现单选按钮。
This really isn't too hard. In your onCreate(), set your RadioButtons with their own onClick() so that when they are activated, they setChecked(true) and do the opposite for the other buttons. For example:
这真的不是太难。在您的 onCreate() 中,使用自己的 onClick() 设置 RadioButtons,以便在激活它们时,它们 setChecked(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()
}
Yeah, I know--way old-school. But it works. Good luck!
是的,我知道——老派。但它有效。祝你好运!
回答by lostdev
Use this class that I created. It will find all checkable children in your hierarchy.
使用我创建的这个类。它将在您的层次结构中找到所有可检查的孩子。
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));
}
}
}
回答by infografnet
Well, I wrote this simple class.
好吧,我写了这个简单的类。
Just use it like this:
只需像这样使用它:
// add any number of RadioButton resource IDs here
GRadioGroup gr = new GRadioGroup(this,
R.id.radioButton1, R.id.radioButton2, R.id.radioButton3);
or
或者
GRadioGroup gr = new GRadioGroup(rb1, rb2, rb3);
// where RadioButton rb1 = (RadioButton) findViewById(R.id.radioButton1);
// etc.
You can call it in onCreate() of Activity for example. No matter which RadioButton
you click, the others will become unchecked. Also, no matters, if some of RadioButtons
are inside of some RadioGroup
, or not.
例如,您可以在 Activity 的 onCreate() 中调用它。无论RadioButton
您单击哪个,其他的都将变为未选中状态。另外,没关系,如果有些RadioButtons
在 some 里面RadioGroup
,或者不在里面。
Here's the class:
这是课程:
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);
}
}
};
}
回答by Ivan Ku?t
Here's my solution based on @lostdev solutionand implementation of RadioGroup
. It's a RadioGroup modified to work with RadioButtons (or other CompoundButtons) that are nested inside child layouts.
这是我基于@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);
}
}
}
}
You can use it in your layout the same way as you would a regular RadioGroup
with the exception that it works with nested RadioButton
views as well:
您可以在布局中以与常规相同的方式使用它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>
回答by viz
Sigh.. Really blame that Android lacks such a basic functionality.
唉..真怪安卓缺少这么基本的功能。
Adapted from @ScottBiggs answer, here's the possibly shortest way to do it with Kotlin:
改编自@ScottBiggs 的回答,这是使用 Kotlin 实现的可能最短的方法:
var currentSelected = button1
listOf<RadioButton>(
button1, button2, button3, ...
).forEach {
it.setOnClickListener { _ ->
currentSelected.isChecked = false
currentSelected = it
currentSelected.isChecked = true
}
}
回答by Pankaj
This solution has not been posted so posting :
此解决方案尚未发布,因此发布:
Step 0: Create a CompoundButton previousCheckedCompoundButton;
as global variable.
第 0 步:创建一个CompoundButton previousCheckedCompoundButton;
作为全局变量。
Step 1: Create OnCheckedChangedListener
for radio buttons
第 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;
}
}
};
Step 3: add listener to all radio buttons:
第 3 步:为所有单选按钮添加侦听器:
radioButton1.setOnCheckedChangeListener(onRadioButtonCheckedListener);
radioButton2.setOnCheckedChangeListener(onRadioButtonCheckedListener);
radioButton3.setOnCheckedChangeListener(onRadioButtonCheckedListener);
radioButton4.setOnCheckedChangeListener(onRadioButtonCheckedListener);
Thats it!! your'e done.
就是这样!!你完成了。
回答by Luccas Correa
I created these two methods to solve this problem. All you have to do is pass the ViewGroup where the RadioButtons are (could be a RadioGroup, LinearLayout, RelativeLayout, etc.) and it sets the OnClick events exclusively, that is, whenever one of the RadioButtons that is a child of the ViewGroup (at any nested level) is selected, the others are unselected. It works with as many nested layouts as you would like.
我创建了这两种方法来解决这个问题。您所要做的就是传递 RadioButtons 所在的 ViewGroup(可以是 RadioGroup、LinearLayout、RelativeLayout 等),并且它专门设置 OnClick 事件,也就是说,只要是 ViewGroup 的子级的 RadioButtons 之一(在任何嵌套级别)被选中,其他未被选中。它适用于您想要的任意数量的嵌套布局。
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;
}
}
Usage inside an activity would be like this:
活动中的用法如下:
ViewGroup parent = findViewById(R.id.radios_parent);
Utils.setRadioExclusiveClick(parent);
回答by madx
You need to do two things:
你需要做两件事:
- Use
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- Make your custom row view implement
Checkable
.
- 用
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- 使您的自定义行视图实现
Checkable
。
So I think that the better solution is to implement Checkable inside your inner LinearLayout: (thanks to daichan4649, from his link, https://gist.github.com/daichan4649/5245378, I took all the code pasted below)
所以我认为更好的解决方案是在你内部的 LinearLayout 中实现 Checkable :(感谢 daichan4649,从他的链接https://gist.github.com/daichan4649/5245378,我把下面粘贴的所有代码)
CheckableLayout.java
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
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
测试片段.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;
}
回答by Egis
I've written my own radio group class that allows to contain nested radio buttons. Check it out. If you find bugs, please let me know.
我已经编写了自己的单选组类,它允许包含嵌套的单选按钮。一探究竟。如果您发现错误,请告诉我。
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);
}
}
回答by umerk44
I've face the same problem as I want to place 4 different radio button in two different linearlayout and these layout will be the child of radio group. To achieve the desire behavior in RadioGroup I have overloaded the addView function
我遇到了同样的问题,因为我想在两个不同的线性布局中放置 4 个不同的单选按钮,这些布局将是单选组的子项。为了实现 RadioGroup 中的期望行为,我重载了 addView 函数
Here is the solution
这是解决方案
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);
}
}
}