Custom Toggle Button

While Android offers a toggle button already, it only has two states – on and off. There may be times when you want more than these – for instance, I wanted a three state toggle button. So this post will describe how to do so – obviously you can easily modify this to have more than three states if needed. I’ll be covering the more significant parts of code – I’m assuming you know the basics.

View and download the complete working source code here.

First a new file needs to be created, attrs.xml. This should be placed in res/values.

<?xml version="1.0" encoding="utf-8"?>
<resources>
	<declare-styleable name="CustomButtonState">
		<!-- Use one for every state you want to have  -->
        <attr name="state_one" format="boolean" />
        <attr name="state_two" format="boolean" />
        <attr name="state_three" format="boolean" />
	</declare-styleable>
</resources>

This allows the Button class to tie into the UI part of it. As the comment states, you should have one attribute per state.

Now for the actual button class – in this case, I called it TriToggleButton, which extends the Button class. The main variable in this class is _state, which keeps track of the current state of the button with int values 0-2. In addition, three int[]‘s are used to hold on to those attributes created in attrs.xml. Again, you will need one of these per state you wish to have.

// Get the attributes created in attrs.xml
private static final int[] STATE_ONE_SET =
{
	R.attr.state_one
};

private static final int[] STATE_TWO_SET =
{
	R.attr.state_two
};

private static final int[] STATE_THREE_SET =
{
	R.attr.state_three
};

These are used by the TriToggleButton’s onCreateDrawableState method, which looks at the current state of the button and assigns the correct drawable to it.

// Generate the drawable needed for the current state
@Override
protected int[] onCreateDrawableState(int extraSpace)
{
	// Add the number of states you have
	final int[] drawableState = super.onCreateDrawableState(extraSpace + 3);

	if(_state == 0)
	{
		mergeDrawableStates(drawableState, STATE_ONE_SET);
	}
	else if(_state == 1)
	{
		mergeDrawableStates(drawableState, STATE_TWO_SET);
	}
	else if(_state == 2)
	{
		mergeDrawableStates(drawableState, STATE_THREE_SET);
	}

	return drawableState;
}

Alright, so we have how the button talks to the UI – now for the internals of how it actually works. First, we need to override the performClick() method, which is called whenever the button is pressed, and in return calls the button’s onClickListener. In our case, we also want to change _state, which we do so by calling the nextState() method.

@Override
public boolean performClick()
{
	// Move to the next state
	nextState();

	return super.performClick();
}

In the nextState() method, the value of _state is increased by one. It then checks to see if _state is now greater than 2, since its values should only be 0-2. If this is true, it loops around _state to 0. It finally calls the setButtonText() method.

// Increases state, or loops to 0
public void nextState()
{
	_state++;

	// Loop around if at the last state
	if(_state > 2)
	{
		_state = 0;
	}

	setButtonText();
	showShortToast("ToggleState: " + _state);
}

The setButtonText() method is just a small one to set the number displayed on the button’s UI. If you were creating your own custom drawables for your button, you would not need this method – it’s just because this is a simple example.

// Set the text displayed on the button
private void setButtonText()
{
	switch(_state)
	{
		case 0: this.setText("1");
				break;
		case 1: this.setText("2");
				break;
		case 2: this.setText("3");
				break;
		default: this.setText("N/A"); // Should never happen, but just in case
				break;
	}
}

You may also note that inside nextState() I call showShortToast(). This isn’t needed as well, I just prefer to use it to keep code neater and easier to read.

// A method just to make using Toasts easier
private void showShortToast(String s)
{
	Toast.makeText(this.getContext(), s, Toast.LENGTH_SHORT).show();
}

And that covers the more important code in TriToggleButton. Note that in the full code I have the constructors, as well as methods to go to the previous state or set a state to a specific value. While I do not use these methods, they’re added for flexibility.

Alright, now to add it to our layout xml file – in my case, this is the default main.xml. Adding a custom UI element isn’t that different from any other, simply that the name is longer. You can still adjust its attributes like how large the button is and its location. Do make sure to give it an id though – we’ll need that to give it an onClickListener.

<com.androidasilearnit.custombuttonexample.TriToggleButton
	android:id="@+id/triToggleButton"
	android:layout_width="50dip"
	android:layout_height="50dip"
	android:layout_gravity="center_horizontal">
	</com.androidasilearnit.custombuttonexample.TriToggleButton>

All that’s left is to go into the main Activity’s onCreate() method, grab the button through findViewById(), and set its onClickListener. Then in the onClick() method, you can get the TriToggleButton’s _state, and perform actions accordingly.

// Grab the button from main.xml and set the onClick listener
final TriToggleButton customButton = (TriToggleButton)findViewById(R.id.triToggleButton);
customButton.setOnClickListener(new View.OnClickListener()
{
	// Runs when the user touches the button
	@Override
	public void onClick(View v)
	{
		// Same as TriToggleButton's _state
		// Will be 0, 1, or 2
		int state = customButton.getState();

		try
		{
			switch(state)
			{
				case 0: // Do whatever is needed when the button changes to state 0
					    // It may be calling methods, setting variables,
						// changing the UI, ect.
						break;
				case 1: // Do whatever is needed when the button changes to state 1
						break;
				case 2: // Do whatever is needed when the button changes to state 2
						break;
				default:break; // Should never occur
			}

//		Log.d(DEBUGTAG, "loopButton _state now::  " + loopButton.getState());
		}
		catch (Exception e)
		{
			Log.e(DEBUGTAG, "ERROR:onCreate:customButton:onClick()::  "
					+ e.getMessage());
			StackTraceElement[] stack = e.getStackTrace();
			for(int i=0; i < stack.length; i++)
			{
				Log.e(DEBUGTAG, "     " + stack[i].toString());
			}
		}

	}
});

Note that a try/catch is not required here, but it’s always good to have. I’m not sure if that’s the best method to get the trace, but it’s one method that works at least.

And that’s it! Running this on your phone, you get a simple square button that will move to its next state when you touch it.

——————————————————————————————————————————

View and download the complete working source code here.

References:

About these ads

, ,

  1. #1 by Hans on April 5, 2011 - 12:57 pm

    Thanks, was looking for instructions on how to achieve this :)

  2. #2 by alexandroid on June 26, 2011 - 6:35 pm

    A screenshot would be nice…

  3. #3 by Otto on July 4, 2011 - 4:44 am

    Great Post! works as a charme for me, thank you!

  4. #4 by Vasya Vasyaa on August 9, 2013 - 6:55 am

    Thank you!
    Please tell me how to add an image change for states?

    • #5 by Amy on November 4, 2013 - 8:25 pm

      Sorry for the delayed response – I’ll work on a modified version with that. It might take a bit longer than usual, it’s been a while since I’ve done Android coding.

  1. attrs not resolved or not a fieldCopyQuery CopyQuery | Question & Answer Tool for your Technical Queries,CopyQuery, ejjuit, query, copyquery, copyquery.com, android doubt, ios question, sql query, sqlite query, nodejsquery, dns query, update query, in
  2. attrs not resolved or not a fieldQueryBy | QueryBy, ejjuit, query, query by, queryby.com, android doubt, ios question, sql query, sqlite query, nodejsquery, dns query, update query, insert query, kony, mobilesecurity, postquery, queryposts.com, sapquery,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: