Android: How to get a radiogroup with togglebuttons?
Asked Answered
H

11

47

I want a group of buttons where a user can choose one of them as option. It has to be a radiobuttongroup like behaviour, but I don't want the radio circle to be present. I just want the user to be able to toggle only one of the buttons.

I think I would need someting like a togglegroup.

Does something like this exist in Android?

Haas answered 4/3, 2010 at 13:24 Comment(2)
Still open, but I used another approach with no need for a radiogroup.Haas
What approach did you use?Matrona
J
91

I'd just re-use the RadioGroup like so: (please note the onClick attribute,i.e. a button click will trigger your Activity's onToggle(View) method.

<RadioGroup android:id="@+id/toggleGroup"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="24dp"
            android:orientation="horizontal"
        >

    <ToggleButton android:id="@+id/btn_Letter"
                  android:layout_height="wrap_content"
                  android:layout_width="fill_parent"
                  android:layout_weight="1"
                  android:textSize="14sp"
                  android:textOn="Letter"
                  android:textOff="Letter"
                  android:onClick="onToggle"
                  android:checked="true"
            />
    <ToggleButton android:id="@+id/btn_A4"
                  android:layout_height="wrap_content"
                  android:layout_width="fill_parent"
                  android:layout_weight="1"
                  android:textSize="14sp"
                  android:textOn="A4"
                  android:textOff="A4"
                  android:onClick="onToggle"
            />
</RadioGroup>

In your Activity, or some place else, you can define a listener, e.g.

static final RadioGroup.OnCheckedChangeListener ToggleListener = new RadioGroup.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(final RadioGroup radioGroup, final int i) {
            for (int j = 0; j < radioGroup.getChildCount(); j++) {
                final ToggleButton view = (ToggleButton) radioGroup.getChildAt(j);
                view.setChecked(view.getId() == i);
            }
        }
    };

and register it, for instance in onCreate():

@Override
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     this.setContentView(R.layout.scan_settings);

     ((RadioGroup) findViewById(R.id.toggleGroup)).setOnCheckedChangeListener(ToggleListener);    
 }

finally in onToggle(View), you would do whatever needs to happen, specific to your app. and also call the RadioGroup's check method, with the toggled view's id. Like so:

public void onToggle(View view) {
    ((RadioGroup)view.getParent()).check(view.getId());
    // app specific stuff ..
}
Jessikajessup answered 29/4, 2011 at 22:12 Comment(8)
I suggest adding ((RadioGroup)view.getParent()).check(0); before setting the new actual value in onToggle. This will prevent the toggle funcionalities of the ToggleButton and force it to behave like a RadioButton with the help of ToggleListener.Vogue
Thanks...just what I was looking for since I could get rid of the stupid round radiobutton circle when trying to style that. It's important to note that if you're styling the buttons, remember to have one w/ android:state_checked="true" or it'll look like the buttons aren't doing anything :-PScotland
Inside the radio check listener, you should do if(view.getId == i) { view.setChecked(true) } else { view.setChecked(false); } Then that way, it imitates a real radio group.Grandsire
I used this technique to mimic tabs, it's much easier to style the tabs this way. Note if you have other views inside the RadioGroup (for cosmetic purposes perhaps?) radioGroup.getChildAt(j) will crash with ClassCastException for obvious reasons (because not all children will be ToggleButtons). Just catch the exception, you will be fine.Fredenburg
If the point of all this is just to have a radio group with options styled as checkboxes, please see dhaag23's answer before implementing this. This solution is twisting the API when Android allows you to simply style the radio options instead.Woolpack
@Wolf Paulus, in my onCreate() method ToggleListener cannot be resolvedStenosis
Something I don't understand.. If you got that listener that does the actual "radio button behavior" then what exactly does the RadioGroup container do? It seems like it would work the same with a LinearLayout?Angelangela
This works great, but it doesn't handle when the same toggle button is checked the unchecked. I added this and it worked great. if(((ToggleButton)view).isChecked()){ //app handle checked }else{ //app handle not checked }Omalley
W
13

You can use regular radio buttons and use an image for the RadioButton background and don't specify a text string:

<RadioButton 
android:id="@+id/custom_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
    android:button="@null"
    android:background="@drawable/button_custom"
    />

For the background, use any drawable, but most likely you'll want to use a selector to be able to provide different images for the different states. The simplest version uses just two images:

<item android:state_checked="true" android:state_pressed="true"
      android:drawable="@drawable/custom_selected" />
<item android:state_checked="false" android:state_pressed="true"
      android:drawable="@drawable/custom_normal" />

<item android:state_checked="true" android:state_focused="true"
      android:drawable="@drawable/custom_selected" />
<item android:state_checked="false" android:state_focused="true"
      android:drawable="@drawable/custom_normal" />

<item android:state_checked="false" android:drawable="@drawable/custom_normal" />
<item android:state_checked="true" android:drawable="@drawable/custom_selected" />

With this, the radio button looks like a regular button (or rather, looks like whatever drawable you provided) and behaves like a radio button in a radio group.

Wearisome answered 3/9, 2010 at 17:28 Comment(2)
But what if I want those buttons fit parent width while the background is stretched uniformly and centered (like scaleType="centerInside" of ImageView)?Bradlybradman
This solution is the cleanest. It maintains the RadioButton functionality while achieving the visible effect of ToggleButton. @Wolf Paulus' solution works, but it allows the group to be in a state where all buttons are off.Masaryk
S
3

I tried all the methods outlined above and none really worked that well.

Trying to hack a radiobutton to look like a real button looks bad.

Eventually I just took the RadioGroup source code and Modified it to accept a ToggleButton rather than a RadioButton. Works really well!

Here is the source on Github: ToggleGroup

Usage:

    <com.rapsacnz.ToggleGroup
    android:id="@+id/deal_detail_toolbar"
    android:layout_width="fill_parent"
    android:layout_height="60dip"
    android:layout_alignParentBottom="true"
    android:background="@drawable/bgnd_toggle_button">
    <ToggleButton
        android:id="@+id/b1"
        android:textOn="@string/tab_1_label"
        android:textOff="@string/tab_1_label"
        android:textSize="10sp"
        android:textColor="@color/tab_button_color"
        android:checked="true"
        android:layout_width="fill_parent"
        android:layout_height="60dp"
        android:layout_weight="1"
        android:drawableTop="@drawable/toggle_spotlight"
        android:drawablePadding="-5dp "
        android:gravity="bottom|center"
        android:paddingTop="10dp"
        android:paddingBottom="5dp"
        android:background="@drawable/bgnd_transparent" />
    <ToggleButton
        android:id="@+id/b2"
        android:textOn="@string/tab_2_label"
        android:textOff="@string/tab_2_label"
        android:textSize="10sp"
        android:textColor="@color/tab_button_color"
        android:checked="false"
        android:layout_width="fill_parent"
        android:layout_height="60dp"
        android:layout_weight="1"
        android:drawableTop="@drawable/toggle_browse"
        android:gravity="bottom|center"
        android:paddingTop="10dp"
        android:paddingBottom="5dp"
        android:background="@drawable/bgnd_transparent" />

    <ToggleButton
        android:id="@+id/b3"
        android:textOn="@string/tab_3_label"
        android:textOff="@string/tab_3_label"
        android:textSize="10sp"
        android:textColor="@color/tab_button_color"
        android:checked="false"
        android:layout_width="fill_parent"
        android:layout_height="60dp"
        android:layout_weight="1"
        android:drawableTop="@drawable/toggle_purchased"
        android:gravity="bottom|center"
        android:paddingTop="10dp"
        android:paddingBottom="5dp"
        android:background="@drawable/bgnd_transparent" />
</com.rapsacnz.ToggleGroup>

Hope this helps

Snowdrop answered 22/3, 2011 at 23:20 Comment(5)
The code you have published here has a small problem, if toggle button @+id/b1 (or any other toggle button) is on (checked) and you press again the button will turn off (unchecked). This behaviour doesn't exists on radio buttons in radio groups. To fix this I've changed class method onCheckedChanged to:Bernstein
// prevents from infinite recursion if (mProtectFromCheckedChange) { return; } int id = buttonView.getId(); mProtectFromCheckedChange = true; if(mCheckedId == id){ //if same if force checked setCheckedStateForView(mCheckedId, true); } else if (mCheckedId != -1) { setCheckedStateForView(mCheckedId, false); } mProtectFromCheckedChange = false; setCheckedId(id);Bernstein
Thanks! I may have done this myself in my production code - forgot to change it here.Snowdrop
I'd love to use this but there seem to be a few issues with it: 1) Trying to use the app with it generates an exception when it goes to inflate it Caused by: android.view.InflateException: Binary XML file line #80: Error inflating class... 2) Can't use it in the LayoutEditor (I added isEditMode() checks to the init() calls, but they didn't help -- Can't instatiate the class, but I suspect that is caused by #1. 3) The above changes don't seem to be in what's there either. Thank you so much for writing it!Bestiary
This is pretty old code - you might want to get a copy of the RadioGroup source code and try again.Snowdrop
M
3

Here is how I managed to do it (no RadioGroup involved):

private CompoundButton.OnCheckedChangeListener toggleListener = new CompoundButton.OnCheckedChangeListener()
{
    boolean avoidRecursions = false;

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
    {
        if(avoidRecursions) return;
        avoidRecursions = true;

        // don't allow the un-checking
        if(!isChecked)
        {
            buttonView.setChecked(true);
            avoidRecursions = false;
            return;
        }

        // un-check the previous checked button
        if(buttonView != toggleButton1 && toggleButton1.isChecked()) toggleButton1.setChecked(false);
        else if(buttonView != toggleButton2 && toggleButton2.isChecked()) toggleButton2.setChecked(false);
        else if(buttonView != toggleButton3 && toggleButton3.isChecked()) toggleButton3.setChecked(false);
        else if(buttonView != toggleButton4 && toggleButton4.isChecked()) toggleButton4.setChecked(false);

        avoidRecursions = false;
    }
};

// ...

toggleButton1.setOnCheckedChangeListener(toggleListener);
toggleButton2.setOnCheckedChangeListener(toggleListener);
toggleButton3.setOnCheckedChangeListener(toggleListener);
toggleButton4.setOnCheckedChangeListener(toggleListener);
Marijane answered 8/4, 2015 at 23:12 Comment(1)
This is pretty much the approach I took - works great, easy to do, and is clear what is happening.Mythify
S
3

I'm using Kotlin and I tried Wolf Paulus's answer and it didn't work well for me. I played around with it and was able to make it work in the following way: First, I removed the "onClick" from the xml:

    <RadioGroup android:id="@+id/toggleGroup"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="24dp"
            android:orientation="horizontal"
        >

    <ToggleButton android:id="@+id/btn_Letter"
                  android:layout_height="wrap_content"
                  android:layout_width="fill_parent"
                  android:layout_weight="1"
                  android:textSize="14sp"
                  android:textOn="Letter"
                  android:textOff="Letter"
                  android:checked="true"
            />
    <ToggleButton android:id="@+id/btn_A4"
                  android:layout_height="wrap_content"
                  android:layout_width="fill_parent"
                  android:layout_weight="1"
                  android:textSize="14sp"
                  android:textOn="A4"
                  android:textOff="A4"
            />
</RadioGroup>

I didn't use the ToggleListener, instead, inside the onCreate() I listened to each of the ToggleButtons I have:

    btn_Letter.setOnClickListener { it -> onToggle(it) }
    btn_A4.setOnClickListener { it -> onToggle(it) }

And the most important part, the onToggle(btn: View)

private fun onToggle(btn: View) {
    val radioGroup = (btn.parent as RadioGroup)
    for (index in 0 until radioGroup.childCount) {
        val child = radioGroup.getChildAt(index) as ToggleButton
        child.isChecked = child.id == btn.id
       // do what ever else you need to do
    }
}

This is it. I hope this helps.

Selfinduction answered 10/1, 2019 at 16:9 Comment(2)
Works and is simple!Santossantosdumont
Simple solution, but it makes using the RadioGroup pointless. You can simply use a LinearLayout or some similar view group.Mechelle
M
1

my solution achieves the same effect but without using the radiobuttons.

For the xml, first un "selector" in "@drawable/myselectorfile":

   <?xml version="1.0" encoding="UTF-8"?>
      <selector
           xmlns:android="http://schemas.android.com/apk/res/android">
       <item android:state_checked="false"
        android:drawable="@drawable/icon_off" />
       <item android:state_checked="true"
        android:drawable="@drawable/icon_on" />
   </selector>

un file for items of my listview:

   <?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="wrap_content"
           android:orientation="horizontal" >

    <TextView
        android:id="@+id/txtInfo"
        android:layout_width="140dp"
        android:layout_height="wrap_content"
        android:paddingTop="15dip" />

   <ToggleButton
       android:id="@+id/BtnToggle"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentRight="true"
       android:layout_alignParentTop="true"
       android:layout_marginRight="14dp"
       android:background="@drawable/toggle_style"
       android:padding="10dip"
       android:textOff=""
       android:textOn="" 
       android:focusable="false"/>  

   </LinearLayout>

and my listview :

   <?xml version="1.0" encoding="utf-8"?>
      <ListView xmlns:android="http://schemas.android.com/apk/res/android"
       android:id="@+id/lv_lista"
       android:layout_width="match_parent"
       android:layout_height="match_parent" >
    </ListView> 

in the oncreate methode:

     lista.setOnItemClickListener(new OnItemClickListener() { 
        @SuppressWarnings("deprecation")
        @Override
        public void onItemClick(AdapterView<?> pariente, View view, int    posicion, long id) {
        @SuppressWarnings("unused")
        ListEntity elegido = (ListEntity) pariente.getItemAtPosition(posicion); 

        varToggle = (ToggleButton) view.findViewById(R.id.BtnToggle);
        varToggle.setChecked(true);

                    // showDialog(0); // dialog optional

                   //restart btn's
                  reStart(posicion);
        }
        });

in the Activity class:

         @Override
         protected Dialog onCreateDialog(int id){
            Dialog dialogo = crearDialogoConfirmacion(); 
            return dialogo; 
         }

         public void reStart(int posicion){ 

         for(int j=0;j<lista.getCount();j++)
             {
               View vista = lista.getChildAt(j);
               ToggleButton varToggle2 = (ToggleButton) vista.findViewById(R.id.BtnToggle);
               if(j != posicion){ varToggle2.setChecked(false); }              
              } 
          }
Majestic answered 20/6, 2013 at 8:22 Comment(0)
G
1

I did it with LinearLayout instead of RadioGroup, using ButterKnife:

@BindViews({R.id.one, R.id.two, R.id.three})
List<ToggleButton> toggleGroup;

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    ButterKnife.bind(this, view);
}

@OnClick({R.id.one, R.id.two, R.id.three})
public void toggle(ToggleButton selected) {
    if (selected.isChecked()) {
        // Deselect other buttons
        for (ToggleButton button : toggleGroup) {
            if (button.getId() != selected.getId()) {
                button.setChecked(false);
            }
        }
    } else {
        // Disallow deselecting the current button
        selected.toggle();
    }
}
Grantland answered 17/3, 2017 at 14:24 Comment(0)
G
0

So you want buttons from that only one can be selected at a time? If you don't show the radio button how does the user know what she has selected?

You can work with RadioButton and RadioGroup but I don't know if you easily can generate a custom radio button with a look more suitable to your app. But it should be possible somehow if you can extend the radiobutton itself and override the draw method, or look what kid of buttons can be integrated in a radiogroup. Maybe you can implement a special interface for your view and then join some of your custom buttons together in one radiogroup.

Gradation answered 4/3, 2010 at 16:9 Comment(0)
B
0

Using any container ViewGroup add your CompoundButtons (CheckBox, RadioButton, Switch, SwitchCompat, ToggleButton) and then inside your onCheckedChanged call a utility function to clear the other buttons.

       <LinearLayout
            android:id="@+id/toggle_group"
            android:orientation="horizontal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <ToggleButton
                android:id="@+id/toggle_btn1"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textOff="Off1"
                android:textOn="On1" />

            <View
                android:layout_width="2dp"
                android:layout_height="match_parent"
                android:layout_margin ="8dp"
                android:background="#ffff" />

            <ToggleButton
                android:id="@+id/toggle_btn2"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textOff="Off2"
                android:textOn="On2" />
        </LinearLayout>

Inside your onCheckedChanged, deal with the state change and call the utility method to clear the other children. The following supports all view container groups which are derived from ViewGroup and allows you to mixed non-CompoundButton objects in the container. Since you often have a onCheckedChanged callback already, there only new code is to call the utility function.

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    int id = buttonView.getId();
    switch (id) {
        case R.id.toggle_btn1:
            if (isChecked) {
                doBtn1Action();
                doRadioGroupChange((ViewGroup)buttonView.getParent(), id);
            }
            break;
        case R.id.toggle_btn2:
            if (isChecked) {
                doBtn2Action();;
                doRadioGroupChange((ViewGroup)buttonView.getParent(), id);
            }
            break;

And here is the utility function to clear children.

/**
 * Emulate radioGroup and clear buttons which don't match checkedId.
 * @param viewGroup
 * @param checkedId
 */
private static void doRadioGroupChange(final ViewGroup viewGroup, final int checkedId) {
    for (int rgIdx = 0; rgIdx < viewGroup.getChildCount(); rgIdx++) {
        View child = viewGroup.getChildAt(rgIdx);
        if (child instanceof  CompoundButton) {
            final CompoundButton view = (CompoundButton) viewGroup.getChildAt(rgIdx);
            view.setChecked(view.getId() == checkedId);
        }
    }
}
Belleslettres answered 4/5, 2017 at 21:42 Comment(0)
C
0
public void OnTabSelected(View v){
        RadioGroup radioGroup = (RadioGroup)v.getParent();
        for (int j = 0; j < radioGroup.getChildCount(); j++) {
            ToggleButton toggleButton = (ToggleButton) radioGroup.getChildAt(j);
            toggleButton.setChecked(v.getId() == toggleButton.getId());
        }
    }
Crimpy answered 19/2, 2018 at 9:48 Comment(0)
B
0

Kotlin version:

for(index in 0..(radioGroup.childCount-1))
                (radioGroup.getChildAt(index) as ToggleButton).isChecked = false
Bendicta answered 14/8, 2018 at 7:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.