3

I want Radio Group implementation on GridView, so that only single item can be selected among the elements of grid.

Please help.

Richa Laad
  • 259
  • 5
  • 21
  • As of now I am trying with an grid_item.xml which contains the radio button. I am trying whether all the radio button of grid items can be added to radio group at run time. I am not sure about this approach. – Richa Laad Oct 08 '12 at 12:54

3 Answers3

11

The purpose to restrict the select of element from the grid can be accomplished as follows:

1.Creation of Grid element.

<LinearLayout
    android:id="@+id/item_layout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical" 
      android:gravity="center">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher" />

    <RadioButton
        android:id="@+id/radiobtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Image" />


</LinearLayout>

2.Inflating this xml in the getView() method of customize adapter.

public class MyAdapter extends BaseAdapter {

    Context mCtx;
    int[] mImg;
    LayoutInflater layoutInflater;
    RadioGroup rgp;
    private RadioButton mSelectedRB;
    private int mSelectedPosition = -1;

    public MyAdapter(Context context, int[] img) {
        this.mCtx = context;
        this.mImg = img;
        rgp = new RadioGroup(context);
        layoutInflater = (LayoutInflater) mCtx
                .getSystemService(LAYOUT_INFLATER_SERVICE);

    }

    @Override
    public int getCount() {

        return mImg.length;
    }

    @Override
    public Object getItem(int position) {

        return null;
    }

    @Override
    public long getItemId(int position) {

        return 0;
    }

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

        if (view == null) {
            view = layoutInflater.inflate(R.layout.element, null);
            holder = new Holder();
            holder.image = (ImageView) view.findViewById(R.id.imageView);
            holder.radioButton = (RadioButton) view
                    .findViewById(R.id.radiobtn);
            view.setTag(holder);

        } else {

            holder = (Holder) view.getTag();
        }

        holder.radioButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {

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

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

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

        return view;
    }

}

private class Holder {

    ImageView image;
    RadioButton radioButton;

}
Richa Laad
  • 259
  • 5
  • 21
1

An alternative approach to this is to create your own subclass of RadioButton which has an extra XML attribute (such as group). This specifies (as a string) to which group the button belongs. In the subclass, you then ensure that within any particular group, only one radio button is selected.

You can do this as follows:

First create the file res/values/attrs.xml which contains something like the following:

<resources>
  <declare-styleable name="GroupedRadioButton">
    <attr name="group" format="string"/>
  </declare-styleable>
</resources>

Then create your subclass, GroupedRadioButton:

public class GroupedRadioButton extends RadioButton {

  public GroupedRadioButton(Context context, AttributeSet attrs) {
    super(context, attrs);
    processAttributes(context, attrs);
    setOnClickListener(internalListener, true);
  }

  ...
}

Once fleshed out (see below), you can then use this new class as follows in your layout files:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:custom="http://schemas.android.com/apk/res/com.example.app"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical" >

<com.example.app.GroupedRadioButton
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Option 1"
    custom:group="group1" />

<com.example.app.GroupedRadioButton
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Option 2"
    custom:group="group1" />

<com.example.app.GroupedRadioButton
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Option 3"
    custom:group="group1" />

<com.example.app.GroupedRadioButton
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Option A"
    custom:group="group2" />

<com.example.app.GroupedRadioButton
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Option B"
    custom:group="group2" />

...

The radio buttons can be anywhere in your layout (e.g. in a GridView). Note the xmlns:custom tag is required since we are using a custom attribute.

The layout above will make options 1, 2 and 3 mutually exclusive and options A and B mutually exclusive.

This is achieved by keeping track (statically) of which GroupedRadioButton is currently selected within each group:

public class GroupedRadioButton extends RadioButton {

  private static Map<String, WeakReference<GroupedRadioButton>> buttonMap;

  static {
    buttonMap = new HashMap<String, WeakReference<GroupedRadioButton>>();
  }

  ...
}

Note that we have to be careful here to ensure that we don't keep strong references to the buttons otherwise they will never be garbage collected.

The processAttributes() method specified in the constructor above digs out the group attribute from the XML we specified and sets this as instance data:

private void processAttributes(Context context, AttributeSet attrs) {
  TypedArray attributes = context.obtainStyledAttributes(attrs,
    R.styleable.GroupedRadioButton);
    int attributeCount = attributes.getIndexCount();
    for (int i = 0; i < attributeCount; ++i) {
      int attr = attributes.getIndex(i);
      switch (attr) {
      case R.styleable.GroupedRadioButton_group:
        this.groupName = attributes.getString(attr);
        break;
    }
  }
  attributes.recycle();
}

We define the main OnClickListener for this class.

private OnClickListener internalListener = new OnClickListener() {

  @Override
  public void onClick(View view) {
    processButtonClick(view);
  }
};

which calls:

private void processButtonClick(View view) {
  if (!(view instanceof GroupedRadioButton))
    return;

  GroupedRadioButton clickedButton = (GroupedRadioButton) view;
  String groupName = clickedButton.groupName;
  WeakReference<GroupedRadioButton> selectedButtonReference = buttonMap.get(groupName);
  GroupedRadioButton selectedButton = selectedButtonReference == null ? null : selectedButtonReference.get();
  if (selectedButton != clickedButton) {
    if (selectedButton != null)
      selectedButton.setChecked(false);
    clickedButton.setChecked(true);
    buttonMap.put(groupName, new WeakReference<GroupedRadioButton>(clickedButton));
  }
  if (externalListener != null)
    externalListener.onClick(view);
}

This does two things. It ensures that it deselects the old group button before selecting the new one (assuming the old and new buttons are different). It then calls onClick() on an externalListener which is provided so that users of the class can add their own 'on-click' functionality.

The setOnClickListener() call in the constructor is to our own method as follows:

private void setOnClickListener(OnClickListener listener, boolean internal) {
  if (internal)
    super.setOnClickListener(internalListener);
  else
    this.externalListener = listener;
}

This sets the internalListener as the official OnClickListener and sets instance data as appropriate for the external listener. The View.setOnClickListener() method can then be overridden as follows:

@Override
public void setOnClickListener(OnClickListener listener) {
  setOnClickListener(listener, false);
}

Apologies for the length of this answer but I hope it helps you and others trying to do the same thing. It would of course not be needed at all if a RadioGroup applied recursively to its children!

darrenp
  • 4,265
  • 2
  • 26
  • 22
0

When you select an element of your grid, check that none of the other elements that you want to be in your radio group aren't selected and if they are deselect them...

StuStirling
  • 15,601
  • 23
  • 93
  • 150