Custom Themes для Custom Widgets

в 5:15, , рубрики: Разработка под android

Разрабатывая HoloEverywhere столкнулся с тем, что большинство присылаемых мне вопросов так или иначе относятся к тому, как стилизовать какие-либо виджеты.

То-есть народ особо не понимает сам принцип работы тем и аттрибутов.
Попробуем немного разжевать эту тему.

Для начала: что такое вообще стили? Набор значений для аттрибутов.
А где список этих аттрибутов, как получить их значения?
Styleable-ресурсы.

Давайте пока по старинке: создадим свою вьюху:

package habra.tutorial.customwidget;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;

public class HabraWidget extends View {
    public HabraWidget(Context context) {
        super(context);
    }

    public HabraWidget(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

Вы никогда не задумывались, что это за последний, третий, аргумент в конструкторе? int defStyle?

У вас будет время подумать, а мы пока создадим свой первый аттрибут, значение которого будет лежать в теме для Activity или даже для всего приложения…
Создаем values/attrs.xml (название не принципиальное, можно хоть в strings.xml)

<?xml version="1.0" encoding="utf-8"?>
<resources>
	<attr name="habraWidgetStyle" format="reference" />
</resources>

В Android имена аттрибутов для стилей для виджетов принято задавать именем виджета в camelCase + постфикс Style.
А format задает, как ни странно, формат значений. Оставим тут reference, т.е. ссылку на ресурc, в данном случае — на стиль.

Теперь модифицируем конструкторы нашего виджета. Сейчас они просто вызывают родительский конструктор.
И теперь фича defStyle — он задает имя аттрибута, по умолчанию будет использоваться значения из стиля, на который ссылается этот аттрибут.

    public HabraWidget(Context context) {
        this(context, null);
    }

    public HabraWidget(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.habraWidgetStyle);
    }

Т.е. конструктор HabraWidget(Context) будет вызывать HabraWidget(Context, AttributeSet) со вторым аргументов null,
а он в свою очередь вызывает HabraWidget(Context, AttributeSet, int) с нашим аттрибутом.

Теперь определимся, а что, собственно, наш виджет делать будет? Пусть он рисует простой крест из своих углов:

    private final Paint paint;

    public HabraWidget(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        paint = new Paint();
        paint.setColor(0xFF00EEFF);
        paint.setStyle(Style.FILL_AND_STROKE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawLine(0, 0, getWidth(), getHeight(), paint);
        canvas.drawLine(getWidth(), 0, 0, getHeight(), paint);
    }

Отлично, но цвет линии жестко указан в коде (0xFF00EEFF), вы думаете о том-же, о чем и я? Вынести в стили!
Сначала создадим styleable с аттрибутом… ну, скажем, lineColor:
attrs.xml

	<declare-styleable name="HabraWidget">
		<attr name="lineColor" format="color|reference"/>
	</declare-styleable>

И создадим дефолтовый стиль:
styles.xml

	<style name="HabraWidget">
		<item name="lineColor">#f00</item>
	</style>

И как теперь вытащить цвет?
TypedArray:

    public HabraWidget(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.HabraWidget, defStyle,
                R.style.HabraWidget);
        paint = new Paint();
        paint.setColor(a.getColor(R.styleable.HabraWidget_lineColor, 0xFFFF0000));
        paint.setStyle(Style.FILL_AND_STROKE);
        a.recycle();
    }

0xFFFF0000 — дефолтный цвет. Можно и передавать какой-нибудь ноль, поскольку дефолтовую тему мы таки указали.
Ну и не забываем сделать recycle после того, как вытащили все данные.
Дальше особенность, касаемая создание layout. Мы привыкли, что все аттрибуты задаются через неймспейс android:.
Но поскольку наши аттрибуты не находятся в пакете android, то и неймспейс другой будет.
Приведу пример:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:habra="http://schemas.android.com/apk/res-auto"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:orientation="vertical" >
	<habra.tutorial.customwidget.HabraWidget
		android:layout_width="match_parent"
		android:layout_height="0dp"
		android:layout_weight="1"
		habra:lineColor="#00f" />
	<habra.tutorial.customwidget.HabraWidget
		android:layout_width="match_parent"
		android:layout_height="0dp"
		android:layout_weight="1"
		habra:lineColor="#0f0" />
</LinearLayout>

image

Теперь мы свободно можем задавать цвет линий из разметки.

Автор: Prototik

Источник

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


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js