Объявление стилизуемых атрибутов в Android

Очень мало документации по тегу declare-styleable, с помощью которого мы можем объявлять собственные стили для компонентов. Я нашел этот список допустимых значений для атрибута format тега attr. Хотя это хорошо, но не объясняет, как использовать некоторые из этих значений. Просмотр attr.xml (источник стандартных атрибутов Android), я обнаружил, что вы можете делать такие вещи, как:

<!-- The most prominent text color.  -->
<attr name="textColorPrimary" format="reference|color" />

Очевидно, что атрибут format может быть установлен на комбинацию значений. Предположительно атрибут format помогает синтаксическому анализатору интерпретировать фактическое значение стиля. Затем я обнаружил это в attr.xml:

<!-- Default text typeface. -->
<attr name="typeface">
    <enum name="normal" value="0" />
    <enum name="sans" value="1" />
    <enum name="serif" value="2" />
    <enum name="monospace" value="3" />
</attr>

<!-- Default text typeface style. -->
<attr name="textStyle">
    <flag name="normal" value="0" />
    <flag name="bold" value="1" />
    <flag name="italic" value="2" />
</attr>

Оба они, кажется, объявляют набор допустимых значений для указанного стиля.

Итак, у меня есть два вопроса:

  1. В чем разница между атрибутом стиля, который может принимать одно из набора значений enum, и атрибутом, который может принимать набор значений flag?
  2. Кто-нибудь знает лучшую документацию о том, как работает declare-styleable (кроме реинжиниринга исходного кода Android)?

person Ted Hopp    schedule 16.05.2011    source источник


Ответы (2)


Вот этот вопрос: Определение пользовательских атрибутов с некоторой информацией, но немного.

И этот пост. В нем есть хорошая информация о флагах и перечислениях:

Флаги настраиваемых атрибутов XML

Флаги - это особые типы атрибутов, поскольку им разрешено только очень небольшое подмножество значений, а именно те, которые определены под тегом атрибута. Флаги задаются атрибутом «имя» и атрибутом «значение». Имена должны быть уникальными в пределах этого типа атрибута, но значения не обязательно. Это причина того, что во время развития платформы Android у нас были «fill_parent» и «match_parent», которые отображали одно и то же поведение. Их ценности были идентичны.

Атрибут имени сопоставляется с именем, используемым в месте значения в XML макета, и не требует префикса пространства имен. Следовательно, для «tilingMode» выше я выбрал «center» в качестве значения атрибута. С таким же успехом я мог бы выбрать «растягивать» или «повторять», но не более того. Даже подстановка фактических значений не была бы разрешена.

Атрибут value должен быть целым числом. Выбор шестнадцатеричного или стандартного числового представления зависит от вас. В коде Android есть несколько мест, где используются оба, и компилятор Android с радостью согласится с ними.

Перечисления настраиваемых атрибутов XML

Перечисления используются почти так же, как флаги с одним положением, они могут использоваться взаимозаменяемо с целыми числами. Под капотом Enums и Integer отображаются в один и тот же тип данных, а именно в Integer. Когда перечисления появляются в определении атрибута с целыми числами, они служат для предотвращения «магических чисел», которые всегда плохи. Вот почему у вас может быть «android: layout_width» с размером, целым числом или именованной строкой «fill_parent».

Чтобы поместить это в контекст, предположим, что я создаю настраиваемый атрибут с именем «layout_scroll_height», который принимает либо целое число, либо строку «scroll_to_top». Для этого я бы добавил атрибут целочисленного формата и следовал за ним перечислением:

<attr name="layout_scroll_height" format="integer">  
    <enum name="scroll_to_top" value="-1"/> 
</attr>

Единственное условие при использовании Enums таким образом заключается в том, что разработчик, использующий ваш пользовательский View, может целенаправленно поместить значение «-1» в параметры макета. Это вызовет особую логику «scroll_to_top». Такое неожиданное (или ожидаемое) поведение могло быстро отбросить вашу библиотеку в кучу «устаревшего кода», если значения Enum были выбраны неправильно.


На мой взгляд, реальные значения, которые вы можете добавить к атрибуту, ограничены тем, что вы можете из него получить. Дополнительные советы см. В AttributeSet справочнике по классам здесь.

Вы можете получить:

  • логические (getAttributeBooleanValue),
  • поплавки (getAttributeFloatValue),
  • целые числа (getAttributeIntValue),
  • ints (как getAttributeUnsignedIntValue),
  • и струны (getAttributeValue)
person Aleadam    schedule 16.05.2011
comment
Спасибо за ссылки. Блог со статической типизацией особенно хорош. Это достаточно близко к реальной документации, поэтому я помечаю ее как решенную. - person Ted Hopp; 16.05.2011
comment
@Ted действительно, в этом посте есть отличная информация. В любом случае, если вы не пишете библиотеку многоразовых представлений или не расширяете фреймворк, я бы не стал беспокоиться об этих перечислениях. Булы, числа с плавающей запятой, целые числа и строки могут далеко продвинуть вас для обычных пользовательских представлений. Это, конечно, если бы вопрос был не просто в удовлетворении очень здорового любопытства :) - person Aleadam; 17.05.2011
comment
@Aleadam - Вопрос был продиктован реальным приложением. Я использовал настраиваемые атрибуты, и мне нужно было добавить новый. Оказывается, правильный формат для нового атрибута - это перечисление, но до тех пор, пока я не прочитал предоставленную вами ссылку, я не смог найти никакой информации о разнице между использованием enum и flag. - person Ted Hopp; 17.05.2011
comment
@Ted Я рад, что это было полезно. В этом блоге есть несколько других сообщений, которые кажутся интересными, хотя я просто бегло просматривал их, у меня еще не было времени их прочитать. - person Aleadam; 17.05.2011
comment
+1 для всех, кто ссылается на мой блог. Я действительно пытался разобрать, для чего использовался каждый из этих тегов. Он может быть неполным и, возможно, его следует обновлять со временем, но если кому-то есть что добавить к нему, я все слышу. - person wheaties; 17.05.2011
comment
Что ж, +10 вам за информацию! Пожалуйста, не стесняйтесь добавлять / изменять / удалять что-нибудь @wheaties - person Aleadam; 17.05.2011
comment
Есть ли способ сделать значение перечисления ~ 0 для побитовых операций? - person Chad Bingham; 14.08.2015

Ответ @Aleadam очень полезен, но imho опускает одно важное различие между enum и flag. Первое предназначено для нас, чтобы выбрать одно и только одно значение, когда мы назначаем соответствующий атрибут для некоторого представления. Однако значения последних можно комбинировать с помощью побитового оператора ИЛИ.

Например, в res/values/attr.xml

<!-- declare myenum attribute -->
<attr name="myenum">
    <enum name="zero" value="0" />
    <enum name="one" value="1" />
    <enum name="two" value="2" />
    <enum name="three" value="3" />
</attr>

<!-- declare myflags attribute -->
<attr name="myflags">
    <flag name="one" value="1" />
    <flag name="two" value="2" />
    <flag name="four" value="4" />
    <flag name="eight" value="8" />
</attr>

<!-- declare our custom widget to be styleable by these attributes -->
<declare-styleable name="com.example.MyWidget">
    <attr name="myenum" />
    <attr name="myflags" />
</declare-styleable>

В res/layout/mylayout.xml теперь мы можем сделать

<com.example.MyWidget
    myenum="two"
    myflags="one|two"
    ... />

Таким образом, перечисление выбирает одно из возможных значений, а флаги можно комбинировать. Числовые значения должны отражать эту разницу, обычно вы хотите, чтобы последовательность использовалась 0,1,2,3,... для перечислений (например, для использования в качестве индексов массива) и флагов для перехода 1,2,4,8,..., чтобы их можно было независимо добавлять или удалять, используя побитовое ИЛИ | для объединения флаги.

Мы могли бы явно определить «метафлаги» со значениями, не равными степени двойки, и, таким образом, ввести своего рода сокращение для общих комбинаций. Например, если бы мы включили это в нашу myflags декларацию

<flag name="three" value="3" />

тогда мы могли бы написать myflags="three" вместо myflags="one|two" для получения полностью идентичных результатов, как 3 == 1|2.

Лично мне нравится всегда включать

<flag name="none" value="0" /> <!-- or "normal, "regular", and so on -->
<flag name="all" value="15" /> <!-- 15 == 1|2|4|8 -->

что позволит мне снять или установить сразу все флаги.

Более тонко, это может быть случай, когда один флаг подразумевается другим. Итак, в нашем примере предположим, что установленный флаг eight должен принудительно установить флаг four (если это еще не было). Затем мы могли бы переопределить eight, чтобы предварительно включить, так сказать, флаг four,

<flag name="eight" value="12" /> <!-- 12 == 8|4 -->

Наконец, если вы объявляете атрибуты в проекте библиотеки, но хотите применить их в макетах другого проекта (в зависимости от библиотеки), вам потребуется использовать префикс пространства имен, который необходимо привязать к корневому элементу XML. Например.,

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:auto="http://schemas.android.com/apk/res-auto"
    ... >

    <com.example.MyWidget
        auto:myenum="two"
        auto:myflags="one|two"
        ... />

</RelativeLayout>
person Rad Haring    schedule 12.03.2014
comment
Хорошие моменты. Я полагаю, что для кого-то, знакомого с перечислениями из языков программирования, разница между флагом и перечислением будет второй натурой. (Я даже не осознавал, что в ответе Алеадама отсутствует то, о чем вы говорили.) Это определенно полезная дополнительная информация. +1 - person Ted Hopp; 13.03.2014
comment
Этот ответ четко указывает на то, как отличать перечисления от флагов при определении настраиваемых атрибутов. Это явно помогло мне использовать флаги :) +1 - person ptitvinou; 14.10.2015