如何在ListView自定义适配器中使用RadioGroup?

45

我想在我的列表中展示单个选择选项。我在我的listView行中使用RadioButton。我知道RadioGroup用于单选。

但问题是我已经将RadioButton添加到了我的ListRowView中。现在我想在一个RadioButton中添加所有的列表项。我正在使用Custom Adapter,并且在getView()中获取RadioButton,但是当我尝试将它添加到RadioGroup中时,它会报错:

"view already have parent , call removeView() in parent before"

我知道这是正确的,但如果我从视图中删除它,那么它就不可见了。

我还尝试以编程方式创建和添加RadioButton,然后将其添加到RadioGroup中,再添加到列表行的视图中。但这次由于父级是RadioGroup,所以又出现了相同的错误:

"view already have parent , call removeView() in parent before"

我想做的是一次只选择一个列表项。我的代码如下。

getView

 public class MyAdapter extends ArrayAdapter < MyMenuItem > {
    
    private LayoutInflater mInflater ;
    
    int                    mResource ;
    List < MyMenuItem >    mData ;
    Context context;
    
    public MyAdapter ( Context context , int resource , int textViewResourceId , List < MyMenuItem > data ) {
        super ( context , resource , textViewResourceId , data ) ;
        this.context = context;
        mData = data ;
        mResource = resource ;
        mInflater = ( LayoutInflater ) getSystemService ( Context.LAYOUT_INFLATER_SERVICE ) ;
    }
    
    @ Override
    public View getView ( int position , View convertView , ViewGroup parent ) {
        ViewHolder holder = null ;
        if ( convertView == null ) {
            convertView = mInflater.inflate ( mResource , null ) ;
            holder = new ViewHolder ( ) ;
            holder.icon = ( ImageView ) convertView.findViewById ( R.id.icon ) ;
            holder.text = ( TextView ) convertView.findViewById ( R.id.text ) ;
            holder.comment = ( TextView ) convertView.findViewById ( R.id.comment ) ;
            LinearLayout lin = ( LinearLayout ) convertView.findViewById ( R.id.linerList ) ;
            RadioButton rbtn = new RadioButton ( context );
            LayoutParams lparam = new LayoutParams ( LayoutParams.WRAP_CONTENT , LayoutParams.WRAP_CONTENT );
            rbtn.setSelected ( false );
            holder.check = rbtn;
            //radioGroup.addView ( rbtn );
            lin.addView ( rbtn , 0 );
            
            convertView.setTag ( holder ) ;
        } else {
            holder = ( ViewHolder ) convertView.getTag ( ) ;
        }
        
        holder.text.setText ( mData.get ( position ).getText ( ) ) ;
        holder.comment.setText ( mData.get ( position ).getComment ( ) ) ;
        
        holder.icon.setImageResource ( getApplicationContext ( ).getResources ( ).getIdentifier ( mData.get ( position ).getIcon ( ) ,
                "drawable" , getPackageName ( ) )

        ) ;
        
        return convertView ;
    }
    
}

行的XML

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<LinearLayout
    android:id = "@+id/linerList"
    android:orientation="horizontal"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="6dip" />
</LinearLayout>
<LinearLayout
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_weight="1"
    android:layout_height="fill_parent">
    <TextView
        android:id="@+id/text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center_vertical"
        android:text="My Application"
        android:textSize="20sp"
        android:singleLine="true"
        android:ellipsize="marquee"
        android:textColor="@color/white" />
    <TextView
        android:id="@+id/comment"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:singleLine="true"
        android:ellipsize="marquee"
        android:text="Simple application that shows how to use RelativeLayout"
        android:textSize="14sp"
        android:textColor="@color/light_gray" />
</LinearLayout>

It look like this if I not use RadioGroup

8个回答

46

您需要完成两件事:

  1. 使用mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
  2. 让您的自定义行视图实现Checkable接口。(有关更多信息,请单击此处)。

所以没有办法使用单选按钮组(Radio Group)。但是要为这种情况编写自定义视图。我也没有期望有什么不同。感谢您的时间。 :) - Arslan Anwar
@Arslan:是的,在您的情况下,您只需要扩展LinearLayout,实现Checkable接口,这些方法将返回您的RadioButton实现。 - Macarse
3
有没有一个完整的示例,可以展示如何在自定义适配器中使用单选按钮,并始终只能有一个被选中?我已经搜索了很久。 - Eugene van der Merwe

38

这个解决方案可行且非常干净,但可能会有更好的解决方案。

您应该使用适配器来管理单选按钮状态。

您必须保留对上次选中的单选按钮的引用,然后在 RadioButton.onClick 中将上次选中的单选按钮设置为 setChecked(false)

还要记得将新选中的单选按钮设置为最后选中的单选按钮。

参见示例:

private class MyAdapter extends ArrayAdapter<String>{

    private int mResourceId = 0;
    private LayoutInflater mLayoutInflater; 
    private RadioButton mSelectedRB;
    private int mSelectedPosition = -1;

    public MyAdapter(Context context, int resource, int textViewResourceId, List<String> objects) {
        super(context, resource, textViewResourceId, objects);
        mResourceId = resource;
        mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }


    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        View view = convertView;
        ViewHolder holder;

        if(view == null){

            view = mLayoutInflater.inflate(mResourceId, parent, false);
            holder = new ViewHolder();

            holder.name = (TextView)view.findViewById(R.id.text);
            holder.radioBtn = (RadioButton)view.findViewById(R.id.radioButton1);

            view.setTag(holder);
        }else{
            holder = (ViewHolder)view.getTag();
        }


        holder.radioBtn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {

                if(position != mSelectedPosition && mSelectedRB != null){
                    mSelectedRB.setChecked(false);
                }

                mSelectedPosition = position;
                mSelectedRB = (RadioButton)v;
            }
        });


        if(mSelectedPosition != position){
            holder.radioBtn.setChecked(false);
        }else{
            holder.radioBtn.setChecked(true);
            if(mSelectedRB != null && holder.radioBtn != mSelectedRB){
                mSelectedRB = holder.radioBtn;
            }
        }




        holder.name.setText(getItem(position));


        return view;
    }

    private class ViewHolder{
        TextView        name;
        RadioButton     radioBtn;
    }
}

希望这对你有所帮助。


@inistel 它正在工作,但我有一个“确认页面”,显示所选的单选按钮,并具有返回到上一页的选项。问题是,当我再次选择另一个单选按钮时,它不再起作用了。已经选择了两个单选按钮。 - Compaq LE2202x
1
这在我第一次点击时有效,当我第二次点击同一个按钮时,不起作用。 - Issac Balaji
好的解决方案,救了我的一天。 - Aditya Vyas-Lakhan
如果您使用了单选按钮组,您必须将其移除。当您移除单选按钮组后,它将被修复。 - Deniz

15

这是我的解决方案。非常简单。

my_radio_adapter_item.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <TextView 
        android:id="@+id/name"
        android:layout_width="0dp" 
        android:layout_height="wrap_content"
        android:layout_weight="1"
        ... />

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

</LinearLayout>

我的RadioAdapter.java

public class MyRadioAdapter extends BaseAdapter
{
    private Context mContext;
    private ArrayList<Variation> mVariations;
    private int mSelectedVariation;


    public MyRadioAdapter(Context context, ArrayList<Variation> variations, int selectedVariation)
    {
        mContext = context;
        mVariations = variations;
        mSelectedVariation = selectedVariation;
    }


    @Override
    public View getView(final int position, View convertView, ViewGroup parent)
    {
        View view = convertView;
        if(view==null) 
        {
            LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.my_radio_adapter_item, null);          
        }

        final Variation variation = mVariations.get(position);

        TextView name = (TextView) view.findViewById(R.id.name);
        RadioButton radio = (RadioButton) view.findViewById(R.id.radio);

        name.setText(variation.getName());
        if(position==mSelectedVariation) radio.setChecked(true);
        else radio.setChecked(false);

        view.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                mSelectedVariation = position;
                MyRadioAdapter.this.notifyDataSetChanged();
            }
        });

        return view;
    }

    ...
}

列表视图在某些片段或活动中被引用,并且它只使用此适配器。 - petrnohejl
你没有理解问题。当你向列表视图添加项目时,就不能将其添加到单选按钮组中,反之亦然。 - Arslan Anwar
谢谢你的回答,帮了我很多......我很感激你的工作。 - Aditya Hari Kishan
我认为这有点粗糙而且不太高效,但作为一个快速解决方案对我很有帮助。+1 - Leo DroidCoder
请问,如果您有几个水平排列的单选按钮,如何管理它们? - Samir

2
你可以放置一个
private int selectedIndex = -1;

然后,在getView代码中,您可以检查
if (position == selectedIndex) {
     rbtn.setSelected ( true );
}
else {
     rbtn.setSelected ( false );
}

并在您的自定义适配器中添加一个方法:

public void setSelectedIndex(int index) {
    //some range-checks, maybe
    selectedIndex = index;
    //invalidate
}

然后,在您的onItemClickedListener中,您调用setPosition上的setSelectedIndex。

我也尝试了这种方法,但问题是getView()方法会被多次调用(为了测量宽度和高度)。 对于我而言,它被调用了很多次。如果我选择第二行,那么它将显示列表中从底部开始的相反的行,而不是从顶部开始的行。 :( - Arslan Anwar

1

我已经检查过了,没问题。但是它没有给我上面的视图,只有一个带复选框的文本视图。我不使用这个,我需要使用上面的视图来完成这个任务。 - Arslan Anwar
我认为如果把单选按钮放在右边会更好看。而且我也会使用drawableLeft来代替额外的ImageView。这是我的意见 :) - Dominic
1
@Arslan:使用CheckedTextView会起作用,因为它实现了Checkable接口,你需要让你的自定义视图实现该接口。 - Macarse
查看AlertDialog源代码和setSingleChoiceItems()函数。 - Dominic

0
你可以尝试这个解决方案。定义一个变量来获取当前选定的位置,并在自定义适配器内执行以下操作。
 singleRadio.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            checkedButtonPosition = position;
  notifyDataSetChanged();
        }
    });

    if (checkedButtonPosition == position){
        singleRadio.setChecked(true);
    }else {
        singleRadio.setChecked(false);
    }

0

除了已经提供的答案之外,为了更加模块化,可以让所有列表项都继承相同的类/接口,该类/接口具有消息函数,通过适配器可以向它们传递消息。
适配器会在每个添加到其中的项中注册自己,因此每个列表项都可以调用适配器发送消息。 然后,在RadioButton监听器中,您可以向所有其他单选按钮发送消息以关闭它们,打开按下的那一个,最后通知适配器数据集已更改。

我有一个ListAdapter类和一个ListItem抽象类,它们非常强大;我可以发给任何需要的人。
支持多种列表项类型。


-1

你需要相同的输出还是不同的输出...

我的意思是你需要从列表中选择一种语言。对吗?

确认一下。这样我就可以给你一个例子。


你应该进行注释而不是回答。 我想一次选择一个。我知道可以通过其他方式完成这个操作。暂时我正在使用其他方法。但这正是我真正需要知道的:“如何在ListView自定义适配器中使用RadioGroup?” - Arslan Anwar
然后我们在列表活动中没有使用单选按钮组。我们将使用一些属性来设置默认列表,例如setchoicemode。但是使用这个属性你不能自定义视图。 - harish
是的,我知道我们可以使用自定义列表。但这样看起来不像上面的图像。我正在以其他方式进行。 - Arslan Anwar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接