📜 ⬆️ ⬇️

Android - Custom View or ToggleButton 4x4

Sooner or later, any novice android developer no longer satisfies the standard set of controls. This refers to both the appearance and functionality. And if with the appearance everything is more or less clear, everything is fairly easy to customize, then the functionality is often not enough.

When (for quite a long time) in one of my projects I was faced with the need to choose one of three parameters in the settings, the decision was obvious - RadioButton. But for several reasons, such as saving space on the screen and some others, there was a desire to use something like the ToggleButton. Since the standard Toggle has only two states, a crutch was used in the form of software processing of some cyclically varying variable, depending on which the properties of a standard element like the usual Button or ImageButton changed - I don’t even remember. The method is quite workable, but not without sin. The first and most important is the violation of the general line of the party, calling for the separate storage of resources and program code. Well, with a large number of similar controls, the code loses all its elegance and appeal. Encapsulation, again, suffers terribly. Therefore, it was decided to create a custom element.

The presented example does not constitute anything difficult for an experienced developer, however, at one time I would be very happy to find something like this in one place, and not to collect it in pieces. Let's try to create a custom ToggleButton that has an arbitrary number (in this case, four) cyclically switching states. Depending on the state in the example above, the inscription on the button and the status icon on the left change (the button's DrawableLeft). However, everything is so simple and transparent that it is easy to apply a state change to any properties of the control.

The first thing we need is to describe the properties we need in the res / values ​​/ attrs.xml file. As we have already decided, these will be four lines and four images for different states of the control:
')
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CustomToggle"> <attr name="customState" format="integer" /> <attr name="customText_0" format="string" /> <attr name="customText_1" format="string" /> <attr name="customText_2" format="string" /> <attr name="customText_3" format="string" /> <attr name="customImage_0" format="reference" /> <attr name="customImage_1" format="reference" /> <attr name="customImage_2" format="reference" /> <attr name="customImage_3" format="reference" /> </declare-styleable> </resources> 

Now we can go directly to the code of the new class. We inherit it from the usual Button.

 public class CustomToggle extends Button implements OnClickListener { private static final int STATE_COUNT = 4; private int state; private String[] txt = new String[3]; private Drawable[] img = new Drawable[3]; private OnViewChangeListener listener; public interface OnViewChangeListener { public void onChangeState(int i); } public void setOnViewChangeListener(OnViewChangeListener l) { this.listener = l; } public CustomToggle(Context context) { this(context, null); } public CustomToggle(Context context, AttributeSet attrs) { this(context, attrs, 0); handleAttributes(context, attrs); } public CustomToggle(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setOnClickListener(this); } private void handleAttributes(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomToggle); final int count = a.getIndexCount(); //     //     for (int i = 0; i < count; i++) { int attr = a.getIndex(i); switch (attr) { case R.styleable.CustomToggle_customText_0: txt[0] = a.getString(attr); break; case R.styleable.CustomToggle_customText_1: txt[1] = a.getString(attr); break; case R.styleable.CustomToggle_customText_2: txt[2] = a.getString(attr); break; case R.styleable.CustomToggle_customText_3: txt[3] = a.getString(attr); break; case R.styleable.CustomToggle_customImage_0: img[0] = a.getDrawable(attr); break; case R.styleable.CustomToggle_customImage_1: img[1] = a.getDrawable(attr); break; case R.styleable.CustomToggle_customImage_2: img[2] = a.getDrawable(attr); break; case R.styleable.CustomToggle_customImage_3: img[3] = a.getDrawable(attr); break; } } a.recycle(); setValues(); } //      private void setValues() { setText(txt[state]); setCompoundDrawablesWithIntrinsicBounds(img[state], null, null, null); } public void setState(int i) { this.state = i; setValues(); } private void changeState() { if (state < STATE_COUNT) state++; else state = 0; setValues(); listener.onChangeState(state); } public int getState() { return state; } public String getCurrentText() { return txt[state]; } @Override public void onClick(View v) { changeState(); } } 

I lowered imports to save, if that - Eclipse tells.

Now that the new class has been created, we can add it to the markup and canonically specify properties in the xml file, without forgetting to add our own namespace:

 <RelativeLayout xmlns:android=http://schemas.android.com/apk/res/android xmlns:custom_toggle="http://schemas.android.com/apk/res/com.example" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.CustomToggle android:id="@+id/custom_toggle" android:layout_width="match_parent" android:layout_height="wrap_content" custom_toggle:customImage_0="@drawable/img_0" custom_toggle:customImage_1="@drawable/img_1" custom_toggle:customImage_2="@drawable/img_2" custom_toggle:customImage_3="@drawable/img_3" custom_toggle:customText_0="@string/toggle_0" custom_toggle:customText_1="@string/toggle_1" custom_toggle:customText_2="@string/toggle_2" custom_toggle:customText_3="@string/toggle_3" /> </RelativeLayout> 

As they say, profit.

We got a skeleton, with which we can further pervert somehow. As options: instead of the STATE_COUNT constant, add the corresponding attribute and method of the type setStateCount (), which will allow you to programmatically limit the number of available states. Full scope for imagination.

That's all for it. Thanks for attention.

PS: Added OnViewChangeListener interface to code.

Source: https://habr.com/ru/post/254527/


All Articles