22

In the app I've been working on, I would like to have a multiple-state (in my case, three) toggle button, instead of the two that ToggleButton provides. I've tried to start my own that extends Button, following the CompoundButton source, but quite honestly reading over its source got a bit overwhelming.

Is there a way to do a three-state toggle button using just a selector xml or something, or perhaps another method I haven't thought of? I'm rather at a loss of how to do this.

Adam Wright
  • 48,938
  • 12
  • 131
  • 152
Melde
  • 385
  • 1
  • 2
  • 7

5 Answers5

20

I implemented a multi-state toggle button, the source code is here

This is how it looks:

enter image description here

And it's quite easy to use it:

<org.honorato.multistatetogglebutton.MultiStateToggleButton
    android:id="@+id/mstb_multi_id"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="10dip"
    mstb:values="@array/planets_array" />

In your activity:

MultiStateToggleButton button2 = (MultiStateToggleButton) this.findViewById(R.id.mstb_multi_id);
button2.setOnValueChangedListener(new ToggleButton.OnValueChangedListener() {
    @Override
    public void onValueChanged(int value) {
        Log.d(TAG, "Value: " + value);
    }
});
jlhonora
  • 10,179
  • 10
  • 46
  • 70
  • how did you customized it like in the picture? In your docs you didn't mention any custimizations. – Choletski Sep 18 '16 at 07:01
  • That's an old version, try searching for older commits. But basically you'll have to modify the background drawables. – jlhonora Sep 18 '16 at 12:12
  • @jlhonora thank you for this great code! I was wondering if it's possible to make a transition between toggles, instead of jumping straight from one button to the other, is there a way to transition between them? – amateur programmer Sep 03 '18 at 03:28
  • @jlhonora If I pass value `Yes` from Activity A to B where the toggle button is, how can I set the state of the toggle button with value `Yes` as checked ? – Hoo Feb 20 '19 at 09:31
14

You can create a custom ImageButton to achieve this, you need 3 different images in this case. You can also add more states if you want.

public class FlashButton extends ImageButton {

    public enum FlashEnum {
        AUTOMATIC, ON, OFF
    }

    public interface FlashListener {
        void onAutomatic();
        void onOn();
        void onOff();
    }

    private FlashEnum mState;
    private FlashListener mFlashListener;

    public FlashButton(Context context, AttributeSet attrs) {
        super(context, attrs);

        //Sets initial state
        setState(FlashEnum.AUTOMATIC);
    }


    @Override
    public boolean performClick() {
        super.performClick();
        int next = ((mState.ordinal() + 1) % FlashEnum.values().length);
        setState(FlashEnum.values()[next]);
        performFlashClick();
        return true;
    }


    private void performFlashClick() {
        if(mFlashListener == null)return;
        switch (mState) {
            case AUTOMATIC:
                mFlashListener.onAutomatic();
                break;
            case ON:
                mFlashListener.onOn();
                break;
            case OFF:
                mFlashListener.onOff();
                break;
        }
    }

    private void createDrawableState() {
        switch (mState) {
            case AUTOMATIC:
                setImageResource(R.drawable.ic_flash_auto);
                break;
            case ON:
                setImageResource(R.drawable.ic_flash_on);
                break;
            case OFF:
                setImageResource(R.drawable.ic_flash_off);
                break;
        }
    }


    public FlashEnum getState() {
        return mState;
    }

    public void setState(FlashEnum state) {
        if(state == null)return;
        this.mState = state;
        createDrawableState();

    }

    public FlashListener getFlashListener() {
        return mFlashListener;
    }

    public void setFlashListener(FlashListener flashListener) {
        this.mFlashListener = flashListener;
    }

}
Musa Y.
  • 1,747
  • 18
  • 26
davidforneron
  • 739
  • 9
  • 12
10

You can certainly define a selector to use as a background that has three entries. The question is what button attributes you can use for the selector. You can have two boolean attributes, say A and B, and define the selector in terms of A, B, and default. (A && B will satisfy A, so more properly they could be thought of as A, !A && B, and !A && !B.) You can overload existing attributes (selected, focused, etc.) or, more elegantly, define your own custom attributes using the recipe described in this thread.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • Looking at the thread you linked: I understand step 1, but 2 I have some questions about. In creating a new view, would it be better to extend View, Button, or something else? And are there other methods I should be overloading besides the constructors and onCreateDrawableState()? My apologies if these are basic, this is my first real app. – Melde Jan 28 '11 at 02:50
  • Nevermind, got it working! Thank you for the push in the right direction, I appreciate it. – Melde Jan 30 '11 at 04:53
  • @moonfire is your code proprietary? I'm trying to create a tri-state toggle button too, and would like to see some other solutions to wrap my head around how to do it. – JohnMetta Feb 16 '11 at 16:54
  • 1
    @JohnMetta Sorry for the late answer, I should check here more often. If you still need help, I just made a post about it at [link](http://goo.gl/WQoGA). Or if you just want to download example source code: [link](http://dl.dropbox.com/u/3448760/AndroidAsILearnIt/CustomButtonExample.zip). Hope that helps! – Melde Mar 05 '11 at 21:15
7

Why not use RadioGroup and style radios inside?

 <RadioGroup
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <RadioButton
        android:layout_width="match_parent"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:background="@drawable/your_drawable_selector"
        android:button="@android:color/transparent"
        android:gravity="center_horizontal" //center text
        android:text="text"
         />
...
Aetherna
  • 241
  • 2
  • 9
2

Chip is a very good native option.

 <com.google.android.material.chip.ChipGroup
      android:id="@+id/chip_group"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:singleSelection="true">

      <com.google.android.material.chip.Chip
           android:id="@+id/first_chip"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:gravity="center"
           android:text="Todo"
           android:textAppearance="?android:attr/textAppearance"
           android:textColor="@color/black"
           android:checkable="true"
           style="@style/Widget.MaterialComponents.Chip.Choice"
           app:chipBackgroundColor="@color/colorAccent"/>

           <!-- Second Chip -->
           <!-- Third Chip -->

   </com.google.android.material.chip.ChipGroup>

binding.chipGroup.setOnCheckedChangeListener { chipGroup, i -> 
    when (i) {
        binding.firstChip -> {
            binding.firstChip.setChipBackgroundColorResource(R.color.colorAccent)
        }
        else -> {} 
    }
}
binding.firstChip.isChecked = true  //default

GL

Source

Braian Coronel
  • 22,105
  • 4
  • 57
  • 62