Объявление настраиваемых атрибутов в 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>
Оба из них, кажется, объявляют набор допустимых значений для указанного стиля.
Итак, у меня есть два вопроса:
- В чем разница между атрибутом стиля, который может принимать один из множества
enum
значения и тот, который может взять на себя наборflag
ценности? - Кто-нибудь знает какую-либо лучшую документацию о том, как
declare-styleable
работает (кроме обратного инжиниринга исходного кода Android)?
2 ответа
Здесь есть такой вопрос: определение пользовательских атрибутов с некоторой информацией, но не очень.
И этот пост. Он имеет хорошую информацию о флагах и перечислениях:
Пользовательские флаги атрибутов XML
Флаги - это специальные типы атрибутов, в которых им разрешено только очень небольшое подмножество значений, а именно те, которые определены под тегом атрибута. Флаги задаются атрибутом "name" и атрибутом "value". Имена должны быть уникальными в пределах этого типа атрибута, но значения не должны быть. Это причина того, что в ходе эволюции платформы Android у нас были "fill_parent" и "match_parent", которые отображали одинаковое поведение. Их значения были идентичны.
Атрибут name отображается на имя, используемое в месте значений в XML-макете, и не требует префикса пространства имен. Следовательно, для "tilingMode" выше я выбрал "center" в качестве значения атрибута. Я мог бы так же легко выбрать "растянутый" или "повторяющийся", но больше ничего. Даже замена в фактических значениях не была бы разрешена.
Атрибут value должен быть целым числом. Выбор шестнадцатеричного или стандартного числового представления зависит от вас. В коде Android есть несколько мест, где используются оба, и компилятор Android с радостью согласится и на то и другое.
Перечисления пользовательских атрибутов XML
Перечисления используются практически идентично флагам с одним положением, они могут использоваться взаимозаменяемо с целыми числами. Под капотом Enums и Integer отображаются один и тот же тип данных, а именно Integer. При появлении в определении атрибута целых чисел перечисления служат для предотвращения "магических чисел", которые всегда плохие. Вот почему у вас может быть "android:layout_width" с измерением, целым числом или именованной строкой "fill_parent".
Чтобы поместить это в контекст, давайте предположим, что я создаю пользовательский атрибут с именем layout_scroll_height, который принимает либо целое число, либо строку scroll_to_top. Для этого я бы добавил атрибут формата integer и следовал за ним с помощью enum:
<attr name="layout_scroll_height" format="integer"> <enum name="scroll_to_top" value="-1"/> </attr>
Единственное условие при использовании Enums таким способом заключается в том, что разработчик, использующий ваше пользовательское представление, может целенаправленно поместить значение "-1" в параметры макета. Это вызовет логику особого случая "scroll_to_top". Такое неожиданное (или ожидаемое) поведение может быстро перевести вашу библиотеку в кучу "устаревшего кода", если значения Enum были выбраны неправильно.
На мой взгляд, реальные значения, которые вы можете добавить к атрибуту, ограничены тем, что вы можете получить от него. Проверить AttributeSet
Ссылка на класс здесь для большего количества подсказок.
Вы можете получить:
- логическое (
getAttributeBooleanValue
), - поплавки (
getAttributeFloatValue
), - целые
getAttributeIntValue
), - целые (как
getAttributeUnsignedIntValue
), - и строки (
getAttributeValue
)
Ответ @Aleadam очень полезен, но imho он пропускает одно существенное различие между enum
а также flag
, Первый предназначен для нас, чтобы выбрать одно и только одно значение, когда мы назначаем соответствующий атрибут для некоторого View. Однако последние значения могут быть объединены с использованием побитового оператора ИЛИ.
Например, в 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,...
поэтому они могут быть независимо добавлены или удалены, используя побитовое ИЛИ |
комбинировать флаги.
Мы могли бы явно определить "мета-флаги" со значениями, которые не являются степенью 2, и, таким образом, ввести своего рода сокращение для общих комбинаций. Например, если бы мы включили это в наш 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 -->
Наконец, если вы декларируете атрибуты в проекте библиотеки, но хотите применить их в макетах другого проекта (в зависимости от lib), вам нужно будет использовать префикс пространства имен, который необходимо связать в корневом элементе 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>