Андроид — кастомные View или ToggleButton 4×4

от автора

Рано или поздно любого начинающего андроид-разработчика перестает удовлетворять стандартный набор элементов управления. При этом имеется в виду как внешний вид, так и функциональность. И если с внешним видом все более или менее понятно, все достаточно легко кастомизируется, то функционала часто не хватает.

Когда (уже достаточно давно) в одном из своих проектов я столкнулся с необходимостью выбора в настройках одного из трех параметров, решение было очевидно — RadioButton. Но по ряду причин, типа экономии места на экране и некоторых других, возникло желание использовать нечто вроде ToggleButton. Поскольку стандартный Toggle имеет лишь два состояния, был использован костыль в виде программной обработки некоей циклично меняющейся переменной, в зависимости от которой менялись свойства стандартного элемента вроде обычной Button или ImageButton – уже даже не помню. Способ вполне работоспособный, однако не без греха. Первый и самый главный – нарушается Генеральная линия партии, призывающая к раздельному хранению ресурсов и программного кода. Ну и при большом количестве подобных элементов управления код теряет всю свою изящность и привлекательность. Инкапсуляция, опять же, жутко страдает. Посему было решено создать кастомный элемент.

Представленный пример не представляет из себя ничего сложного для опытного разработчика, однако я в свое время был бы весьма рад найти подобное в одном месте, а не собирать по кусочкам. Давайте попытаемся создать кастомный ToggleButton, имеющий произвольное количество (в данном случае четыре) циклически переключающихся состояний. В зависимости от состояния в приведенном примере меняется надпись на кнопке и значок состояния слева (DrawableLeft у кнопки). Однако все настолько просто и прозрачно, что легко применить изменение состояния к любым свойствам контрола.

Первое, что нам необходимо – описать нужные нам свойства в файле res/values/attrs.xml. Как мы уже решили, это будут четыре строки и четыре изображения для различных состояний элемента управления:

<?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> 

Теперь можем перейти непосредственно к коду нового класса. Отнаследуем его от обычной 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];  	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(); 	}  	public int getState() { 		return state; 	}  public String getCurrentText() { 		return txt[state]; 	}  	@Override 	public void onClick(View v) { 		changeState(); 	} } 

Я опустил импорты для экономии, если что – Эклипс подскажет.

Теперь, когда новый класс создан, мы можем добавить его на разметку и канонично указать свойства в xml-файле, не забыв добавить свое пространство имен:

<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> 

Как говорится, profit.

Мы получили скелет, с которым дальше можем извращаться как угодно. Как варианты: вместо константы STATE_COUNT добавить соответствующие атрибут и метод типа setStateCount(), что позволит программно динамически ограничивать количество доступных состояний. Полный простор для фантазии.

На этом собственно все. Спасибо за внимание.

ссылка на оригинал статьи http://habrahabr.ru/post/254527/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *