Android. Заметка на будущее

в 6:45, , рубрики: android development, java, XML, Разработка под android, метки: , ,

В этой статье хотел бы поделиться своими наблюдениями и опытом в построении приложений под платформу Android. О том, как сохранить время в будущем.

Layouts

Думаю каждый начинающий программист под платформу Android, сразу замечает, чтобы построить какой-либо UI, требуется создать xml файл в директории проекта /res/layout, а потом еще чудесным образом связать его с кодом на Java. По началу это кажется своебразным методом и сразу появлется ощущение того, что чего-то не хватает, и UI дизайнер в плагине Android для IDE далеко не чудо. Ну хватит разглагольствований, к делу!

Если сильно не углубляться в разнообразие устройств на рынке, что в моем случае так и вышло, есть платформа Android какой-то версии, знаем размер экрана целевого устройства. Допустим у вас такая же ситуация.

В общем случае приходиться иметь две папки /res/layout и /res/layout-land. Здесь -land выступает квалификатором (qualifier), который обозначает, что любой layout в этой папке доступен только для Landscape (горизонтального) режима. Если существует layout, который одинакого выглядит для обоих режимов, вертикального и горизонтального, то его помещают в /res/layout. Android самостоятельно вызывает деструктор Activity и создает новое Activity при повороте экрана, если не указана конкретная ориентация в AndroidManifest. Таким образом, можно размещать layout с одним и тем же именем в обоих папках /res/layout и /res/layout-land, а Android позаботится о загрузке актуального layout.

В коде Activity, как обычно, вызывается

setContentView(R.layout.[имя layout]);

Что от меня? И правда, описал в кратце то, что можно и так найти в сети. Дело в том, что мне пришлось писать очень графически изменнное приложение. Большенство элементов были очень изменены. Первое что пришло в голову, было ошибочно. Решил написать собственный дочерний компонент, от того же ListView, к примеру, и там понеслось: onDraw, dispatchDraw и др. Так мне показалось мало, еще и вбил конкретные значения в пикселях при отрисовке какого-либо элемента.

Это то, как не надо делать. Даже если нет выхода, пытайтесь до последнего не создавать компонент, а выкручиваться layout'ами. Намного лучше написать BaseAdapter для ListView, к примеру, где загружать другой layout и его контроллировать. Если выхода нет, то все значения для UI в пикселях, выносить в свойства компонента (attrs), которые будет передаваться при описании компонента в xml. Сами значения в xml, так же не указывать на прямую, а использовать dimensions, ссылки.

Давайте рассмотрим, что я подразумиваю под attrs и dimensions.

Attrs

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

Содержание attrs.xml вполне доступно для чтение человеком. Давайте рассмотрим простой пример:

<?xml version="1.0" encoding="utf-8" ?>
<resources>

	<declare-styleable name="MyExampleView">
		<attr name="exampleAttrWidth" format="dimension" />
	</declare-styleable>

</resources>

Resources встречается постоянно, используется для хранение каких-либо ресурсов и впоследствии доступен в коде Java через статический класс R пакета приложения. К примеру наше объявляение доступно через R.styleable.MyExampleView. По моим наблюдениям и тому, что приходилось использовать, есть такой список format (тип свойства):

  • dimension — может быть значение типа 10px, 10dip или ссылка на @dimen/[имя значения]
  • integer — может быть значение типа 10, 5, 2. Так же думаю, что и ссылка может сработать
  • string — просто текстовое значение типа «Hello World» или ссылка на @string/[имя значения]
  • reference — ссылка на @drawable к примеру, что в свою очередь может быть @drawable, @color или что-то другое

Допустим у нас есть собственный класс, наследник View: com.android.example.view.MyExampleView. Опишем его просто:

package com.android.example.view;

// import

public class MyExampleView extends View {

	private exampleWidth;

	public MyExampleView(Context context) {
		super(context);

		// значение по умолчанию
		this.exampleWidth = 128;
	}

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

		initialize(context, attrs, 0);
	}

	public MyExampleView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

		initialize(context, attrs, defStyle);
	}

	private void initialize(Context context, AttributeSet attrs, int defStyle) {
		// запрашиваем свойства описанные в xml
		final TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.MyExampleView, defStyle, 0);
		try {
			// считываем значение exampleWidth типа dimension в пикселях
			this.verticalSpace = styledAttributes.getDimensionPixelSize(R.styleable.MyExampleView_exampleWidth, 128 /* значение по умолчанию */);
		} finally {
			// сообщаем Android о том, что данный объект можно переиспользовать
			styledAttributes.recycle();
		}
	}
}

Таким образом мы создали собственный элемент управления, который можно настраивать прямо из xml, и данный метод очень гибок, т.к. отсутствует привязка к определенным значениям в коде, кроме по умолчанию. Чтобы создать элемент, напишим в layout:

<!-- в верхнем элементе добавить запись xmlns:example="http://schemas.android.com/apk/res/com.android.example.view" для того, чтобы была возможно писать example:[имя аттрибута] -->

<com.android.example.view.MyExampleView example:exampleWidth="@dimen/exampleWidth" />

<!-- ... -->

Dimensions

В идеальном мире все значения выносить в /res/values/dimensions.xml в формате: [значение]dp, после чего использовать в xml или коде через ссылку на @dimen/[имя]. Я советую, как и поступаю на данный момент, выносить размер текста, главных элементов приложения, к примеру смещения каких-то панелей, padding/margin по умолчанию и др. Не выносить значения для каких-то конкретных элементов, например в одном диалоге кнопка от кнопки на расстоянии в 10 пикселей.

Такой подход поможет быть уверенным, что в приложении весь текст выглядит стандартизированно, например большие заголовки в 30 пикселей, средние в 24, а обычный текст в 16. Если не понравилось — меняем только в одном месте.

В принципе останавливаться на этом долго не стоит, но есть один момент. Недавно Google обновила плагин Android для Eclipse, соответственно, и теперь там есть такой зверь Lint. Так вот, он мне все время подмигивал и убеждал, что надо бы использовать dp, а не px, и расписывал еще по какой такой причине. Я и поверил, сделал. А теперь давайте вспомним о Density. Насколько я понимаю, это значение показывает насколько плотно расположены пиксели друг к другу. Т.е. на пример у вас есть устройство в разрешением в 800x600. Но дело в том, что одно устройство имеет 800 * 600 пикселей, в другое 2 * 800 * 2 * 600. Думаю уловили разницу? Т.е. разрешение одно, но качество и соответственно плотность пикселей совершенно другая. И именно в этом скрывается подвох Lint. После миграции на устройство с большей плотностью, используя dp, у меня все элементы поехали, а текст стал совершенно других размеров (на взгляд).

Как оказалось, используй я с самого начала px везде и игнорируй предупреждения Lint, я бы не тратил дополнительное время на переписывание dp на px.

Colors

Цвета в Android так же могут (должны) быть представлены в xml в форматах: argb, rgb. К примеру, белый цвет:

  • rgb = #fff. Это не #0f0f0f или #f0f0f0 — это сокращенная форма, в итоге имеем непрозрачный белый цвет #ffffffff
  • argb = #ffff. На подобии предыдущего, только включая alpha составляющую
  • rgb = #ffffff. Полная форма rgb
  • argb = #ffffffff. Полная форма argb

В принципе очень схоже на dimensions правила, обычно располагается в /res/values/colors.xml, так же в resources теге. Выносить в colors стоит цвета которые используются для стандартизации полного UI, а не каких-либо мелких деталей одного из элементов. Так сказать золотая серидина между параноей и ленью.

Надеюсь кому то эти заметка сохранят время или помогут в чем-то более глубоко разобраться.

Автор: vladlichonos

Поделиться

* - обязательные к заполнению поля