Объявление атрибутов стиля в 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)?

Ответ 1

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

И этот post. Он имеет хорошую информацию о флажках и перечислениях:

Пользовательские флаги атрибутов XML

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

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

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

Пользовательские перечисления атрибутов XML

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

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

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

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


Как я вижу, реальные значения, которые вы можете добавить в реальность к атрибуту, ограничены тем, что вы можете получить от него. Подробнее о ссылках на AttributeSet здесь.

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

  • booleans (getAttributeBooleanValue),
  • floats (getAttributeFloatValue),
  • ints (getAttributeIntValue),
  • ints (as getAttributeUnsignedIntValue),
  • и строки (getAttributeValue)

Ответ 2

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

Пример, в 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>