Оголошення стильових атрибутів в Android


81

Існує дуже мала документація про 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)?

Відповіді:


74

Тут є таке питання: Визначення власних атрибутів з деякою інформацією, але не так багато.

І цей пост . У ньому є хороша інформація про прапори та переліки:

Спеціальні прапорці атрибутів XML

Прапори - це спеціальні типи атрибутів, оскільки їм дозволено лише дуже малу підмножину значень, а саме ті, які визначені під тегом атрибута. Прапори визначаються атрибутом “name” та “value”. Імена повинні бути унікальними для цього типу атрибута, але значення не повинні бути. Це є причиною того, що під час еволюції платформи Android у нас були "fill_parent" і "match_parent", які відповідали однаковій поведінці. Їх значення були однаковими.

Атрибут name пов'язується з іменем, що використовується у місці значення в XML-макеті, і не вимагає префіксу простору імен. Отже, для “tilingMode” вище я вибрав “center” як значення атрибута. Я міг так само легко вибрати «розтягування» або «повторення», але нічого іншого. Навіть заміщення у фактичних значеннях не було б дозволено.

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

Спеціальні перелічення атрибутів XML

Перелічення використовуються майже однаково, як прапори з одним положенням, вони можуть використовуватися як взаємозамінні з цілими числами. Під капотом Enums і Integers відображаються до одного типу даних, а саме Integer. При появі у визначенні атрибута з цілими числами Enums служить для запобігання «магічним числам», які завжди є поганими. Ось чому ви можете мати “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),
  • ints ( getAttributeIntValue),
  • ints (як getAttributeUnsignedIntValue),
  • і рядки ( getAttributeValue)

Дякую за ці посилання. Блог зі статичним типом особливо приємний. Це досить близько до "реальної документації", що я позначаю це як вирішене.
Тед Хопп,

@ Справді, цей пост має чудову інформацію. У будь-якому випадку, якщо ви не пишете бібліотеку багаторазових переглядів або не розширюєте структуру, я б не турбувався про ці переліки. Bools, floats, ints і strings можуть привести вас далеко до звичайних користувацьких переглядів. Тобто, звичайно, якби питання полягало не просто в тому, щоб задовольнити дуже здорову цікавість :)
Алеадам,

@Aleadam - Питання було мотивоване реальним додатком. Я використовував власні атрибути і мені потрібно було додати новий. Виявляється, правильним форматом для нового атрибута є перелік, але до прочитання посилання, яке ви надали, я не міг знайти ніякої інформації про різницю між використанням enumта flag.
Тед Хопп,

@Ted Я радий, що це було корисно. У цьому щоденнику є кілька інших публікацій, які здаються цікавими, хоча я просто переглядаю їх, ще не встиг їх прочитати.
Алеадам,

3
+1 для всіх, хто посилається на мій блог. Я справді намагався проаналізувати, для чого використовувався кожен із цих тегів. Він може бути не повним, і, можливо, його слід оновлювати з часом, але якщо хтось має що додати до нього, я всі вуха.
Wheaties

70

Відповідь @Aleadam дуже корисна, однак у них опущена одна велика різниця між 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,...їх можна було самостійно додавати або видаляти, використовуючи побітове АБО |для поєднання прапорів.

Ми могли б чітко визначити "мета прапори" зі значеннями, які не мають рівня 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>

Хороші бали. Я припускаю, що для когось, хто знайомий з переліченнями з мов програмування, різниця між прапором і переліком буде другою природою. (Я навіть не підозрював, що в відповіді Алеадама пропало те, що ви висловлювали.) Це, безумовно, корисна додаткова інформація. +1
Тед Хопп,

Ця відповідь чітко вказує на те, як відрізнити перелічення від прапорів, визначаючи власні атрибути. Мені це явно допомогло, використовуючи прапори :) +1
ptitvinou
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.