45

I want to show a single select option in my list. I am using RadioButton in my listView row. I know that RadioGroup is used for single selection.

But problem is that I have added the RadioButton in my ListRowView. Now I want to add all my list items in one RadioButton. I am using Custom Adapter and in getView(). I get the RadioButton in getView(), but when want to add it in RadioGroup it say

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

And I know its true, but if I remove it from the view. Then it is not visible.

I also try to create and add RadioButton programmatically. And then add it in RadioGrop. And then to view of list row. But this time as the parent is RadioGroup, so again it say

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

What I want to do is to select only one item in list at a time. My code is as follows.

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 ;
    }
    
}

My XML for the row

<?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

Community
  • 1
  • 1
Arslan Anwar
  • 18,746
  • 19
  • 76
  • 105

8 Answers8

46

You need to do two things:

  1. Use mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
  2. Make your custom row view implement Checkable. (More info about this here).
Community
  • 1
  • 1
Macarse
  • 91,829
  • 44
  • 175
  • 230
  • So there is not way to use Radio Group. But to write custom view for such a case. I was expecting the same. Thanks for your time. :) – Arslan Anwar Sep 15 '11 at 10:11
  • @Arslan: Yep, in your case you just need to extend `LinearLayout`, implement the `Checkable` interface and those methods will return your `RadioButton` implementation. – Macarse Sep 15 '11 at 12:10
  • 3
    Is there somewhere I can find a complete example of how to have radio button in custom adaptor and always only have one checked? I have searched very hard. – Eugene van der Merwe Jun 17 '12 at 18:07
38

This solution works and it's pretty clean, but there might be some better solutions out there.

You should use your adapter to manage the radio buttons state.

You must keep a reference to the last checked radio button, and then upon RadioButton.onClick you set the last checked radio button setChecked(false).

also remember to set the newly selected radio button as the last selected radio button.

see example:

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;
    }
}

Hope it does it for you.

Ashraf Bashir
  • 9,686
  • 15
  • 57
  • 82
Inon Stelman
  • 955
  • 7
  • 12
15

This is my solution. Its pretty easy.

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>

MyRadioAdapter.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
  • 7,581
  • 3
  • 51
  • 63
2

You could put a

private int selectedIndex = -1;

then, in the getView-code you could check

if (position == selectedIndex) {
     rbtn.setSelected ( true );
}
else {
     rbtn.setSelected ( false );
}

and add a method in your custom adapter:

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

Then, in your onItemClickedListener you call setSelectedIndex on the position.

AmITheRWord
  • 1,363
  • 2
  • 14
  • 36
  • I have try this to. But issue is then getView() call itself multiple times.(To measure width,height). For me it call multiple times. and if I select 2nd row. Then it show the opposite row(from the bottom in the list) not from top. :( – Arslan Anwar Sep 14 '11 at 13:13
1

You need to use the CheckedTextView instead of the normal one. http://developer.android.com/reference/android/widget/CheckedTextView.html

I never used it, but the AlertDialog uses it for the SingleChoice items. So it definitively will work :)

Edit: don't forget to call

 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
Dominic
  • 3,353
  • 36
  • 47
  • I have check this already. Its fine. But It not give me the above view. But only textview with a checkbox. I am not using this. I need to do this using above view. – Arslan Anwar Sep 12 '11 at 06:52
  • I think it looks better if you have the radio buttons on the right side. And I also would use drawableLeft for the TextView and not an extra ImageView. Is my opinion :) – Dominic Sep 12 '11 at 12:23
  • 1
    @Arslan: Using `CheckedTextView` will work since it implements `Checkable`, you need your custom view to implement that interface. – Macarse Sep 14 '11 at 23:28
  • Look at the AlertDialog source and the setSingleChoiceItems() – Dominic Sep 15 '11 at 10:52
0

to add to the answers already given, for a more modular approach, have all list items inherit from the same class/interface that have a message function that would allow you to pass massages to them via the adapter.
The adapter registers itself with every item that is added to it, so every list item can call the adapter to send a message. then, in the RadioButton listener you can send a message to all the other radio buttons to turn off, turn the pressed one on, then finally notify dataset changed on the adapter.

I have an ListAdapter class, and a ListItem abstract class that are fairly robust; I'll send to anyone who needs it.
Multiple list item types supported.

Max Izrin
  • 938
  • 1
  • 11
  • 17
0

You can try this solution. Define a variable to get the currently selected position and do the following inside the custom adapter.

 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);
    }
Mohamed Sajjadh
  • 139
  • 2
  • 11
-1

you need the same output or different...

What i mean is you have select only one of the language from the list. Is it right?

conform it. So that i will give one example for that

harish
  • 255
  • 1
  • 3
  • 13
  • You should comment instead of answer. I want to chose one at a time. I know this can be done via some other way. Temporarily I am doing some other way. But this this is what I really need to know that "How to use RadioGroup in ListView custom adapter?" – Arslan Anwar Sep 09 '11 at 13:20
  • then we did not use radio group for the list activity. We will use some properties for the default list like setchoicemode. But using this you did not customize the view – harish Sep 09 '11 at 13:25
  • Yes I know We can use customize list. But this will not look like the above image. I am doing this in some other manner. – Arslan Anwar Sep 09 '11 at 13:27