Мені потрібно реалізувати власні атрибути, як у com.android.R.attr
В офіційній документації нічого не знайдено, тому мені потрібна інформація про те, як визначити ці attrs та як їх використовувати з мого коду.
Мені потрібно реалізувати власні атрибути, як у com.android.R.attr
В офіційній документації нічого не знайдено, тому мені потрібна інформація про те, як визначити ці attrs та як їх використовувати з мого коду.
Відповіді:
Наразі найкраща документація - це джерело. Ви можете подивитися тут (attrs.xml) .
Ви можете визначити атрибути у верхньому <resources>
або всередині <declare-styleable>
елемента. Якщо я збираюся використовувати attr у більш ніж одному місці, я поміщую його в елемент root. Зауважте, всі атрибути мають однаковий глобальний простір імен. Це означає, що навіть якщо ви створюєте новий атрибут всередині <declare-styleable>
елемента, він може бути використаний поза ним, і ви не можете створити інший атрибут з тим самим іменем іншого типу.
<attr>
Елемент має два атрибути XML name
і format
. name
дозволяє назвати це що - то і це, як ви в кінцевому підсумку з посиланням на нього в коді, наприклад, R.attr.my_attribute
. format
Атрибут може мати різні значення в залежності від «типу» атрибута ви хочете.
Ви можете встановити формат для декількох типів, використовуючи |
, наприклад, format="reference|color"
.
enum
атрибути можна визначити наступним чином:
<attr name="my_enum_attr">
<enum name="value1" value="1" />
<enum name="value2" value="2" />
</attr>
flag
атрибути схожі, за винятком значень, які потрібно визначити, щоб вони могли бути розбиті разом:
<attr name="my_flag_attr">
<flag name="fuzzy" value="0x01" />
<flag name="cold" value="0x02" />
</attr>
Крім атрибутів є <declare-styleable>
елемент. Це дозволяє визначити атрибути, які може використовувати власний вид. Ви робите це, вказуючи <attr>
елемент, якщо він був визначений раніше, ви не вказуєте format
. Якщо ви хочете повторно використовувати attr для android, наприклад, android: gravity, тоді ви можете зробити це в name
наступному.
Приклад спеціального перегляду <declare-styleable>
:
<declare-styleable name="MyCustomView">
<attr name="my_custom_attribute" />
<attr name="android:gravity" />
</declare-styleable>
Визначаючи свої власні атрибути в XML у своєму власному представленні, потрібно зробити кілька речей. Спочатку ви повинні оголосити простір імен, щоб знайти свої атрибути. Ви робите це на елементі макета кореня. Зазвичай є тільки xmlns:android="http://schemas.android.com/apk/res/android"
. Тепер ви також повинні додати xmlns:whatever="http://schemas.android.com/apk/res-auto"
.
Приклад:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:whatever="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<org.example.mypackage.MyCustomView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
whatever:my_custom_attribute="Hello, world!" />
</LinearLayout>
Нарешті, щоб отримати доступ до цього спеціального атрибуту, ви зазвичай робите це в конструкторі власного перегляду наступним чином.
public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0);
String str = a.getString(R.styleable.MyCustomView_my_custom_attribute);
//do something with str
a.recycle();
}
Кінець. :)
View
: github.com/commonsguy/cw-advandroid/tree/master/Views/…
xmlns:my="http://schemas.android.com/apk/lib/my.namespace"
- без копіювання attrs.xml. Зверніть увагу, шлях URI простору імен повинен бути / apk / * lib * not / apk / res.
apk/lib
хитрість не працювала для мене на користувальницьких атрибутах із форматом довідки з бібліотечного проекту. Що ж робота полягала у використанні apk/res-auto
, як це було запропоновано в stackoverflow.com/a/13420366/22904 трохи нижче , а також в stackoverflow.com/a/10217752
enum
і flag
: перше дозволяє нам вибрати одне і лише одне значення, а друге дозволяє об'єднати декілька. Я написав довшу відповідь на подібне запитання тут , і, знайшовши це питання, я зрозумів, що буду посилатися на це.
a.recycle()
Тут дуже важливо звільнити пам'ять
Відповідь Qberticus хороша, але одна корисна деталь відсутня. Якщо ви реалізуєте їх у бібліотеці, замініть:
xmlns:whatever="http://schemas.android.com/apk/res/org.example.mypackage"
з:
xmlns:whatever="http://schemas.android.com/apk/res-auto"
Інакше програма, яка використовує бібліотеку, матиме помилки під час виконання.
Відповідь вище висвітлює все докладно, окрім кількох речей.
По-перше, якщо стилів немає, тоді (Context context, AttributeSet attrs)
підпис методу буде використовуватися для інстанціювання налаштування. У цьому випадку просто використовуйте context.obtainStyledAttributes(attrs, R.styleable.MyCustomView)
для отримання TypedArray.
По-друге, це не стосується способів поводження з ресурсами плауралів (рядкових кількостей). З ними не вдається вирішити використання TypedArray. Ось фрагмент коду з моєї SeekBarPreference, який встановлює резюме перевагу, форматуючи його значення відповідно до значення налаштування. Якщо xml для набору переваг має android: резюме до текстового рядка або ряду resouce, значення переваги відформатоване у рядок (у ньому повинно бути% d, щоб отримати значення). Якщо для android: Summary встановлено ресурс plaurals, то це використовується для форматування результату.
// Use your own name space if not using an android resource.
final static private String ANDROID_NS =
"http://schemas.android.com/apk/res/android";
private int pluralResource;
private Resources resources;
private String summary;
public SeekBarPreference(Context context, AttributeSet attrs) {
// ...
TypedArray attributes = context.obtainStyledAttributes(
attrs, R.styleable.SeekBarPreference);
pluralResource = attrs.getAttributeResourceValue(ANDROID_NS, "summary", 0);
if (pluralResource != 0) {
if (! resources.getResourceTypeName(pluralResource).equals("plurals")) {
pluralResource = 0;
}
}
if (pluralResource == 0) {
summary = attributes.getString(
R.styleable.SeekBarPreference_android_summary);
}
attributes.recycle();
}
@Override
public CharSequence getSummary() {
int value = getPersistedInt(defaultValue);
if (pluralResource != 0) {
return resources.getQuantityString(pluralResource, value, value);
}
return (summary == null) ? null : String.format(summary, value);
}
notifyChanged()
в onDialogClosed
методі уподобань .Традиційний підхід насичений кодовою табличкою та незграбною обробкою ресурсів. Тому я зробив каркас Spyglass . Щоб продемонструвати, як це працює, ось приклад, який показує, як створити спеціальний вид, який відображає заголовок String.
Крок 1. Створіть спеціальний клас перегляду.
public class CustomView extends FrameLayout {
private TextView titleView;
public CustomView(Context context) {
super(context);
init(null, 0, 0);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr, 0);
}
@RequiresApi(21)
public CustomView(
Context context,
AttributeSet attrs,
int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs, defStyleAttr, defStyleRes);
}
public void setTitle(String title) {
titleView.setText(title);
}
private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
inflate(getContext(), R.layout.custom_view, this);
titleView = findViewById(R.id.title_view);
}
}
Крок 2: Визначте атрибут string у values/attrs.xml
файлі ресурсу:
<resources>
<declare-styleable name="CustomView">
<attr name="title" format="string"/>
</declare-styleable>
</resources>
Крок 3: Застосуйте @StringHandler
анотацію до setTitle
методу, щоб повідомити рамці Spyglass для маршрутизації значення атрибуту до цього методу, коли подання завищене.
@HandlesString(attributeId = R.styleable.CustomView_title)
public void setTitle(String title) {
titleView.setText(title);
}
Тепер, коли ваш клас має анотацію Spyglass, рамка Spyglass виявить його під час компіляції та автоматично генерує CustomView_SpyglassCompanion
клас.
Крок 4: Використовуйте створений клас у init
методі користувацького перегляду :
private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
inflate(getContext(), R.layout.custom_view, this);
titleView = findViewById(R.id.title_view);
CustomView_SpyglassCompanion
.builder()
.withTarget(this)
.withContext(getContext())
.withAttributeSet(attrs)
.withDefaultStyleAttribute(defStyleAttr)
.withDefaultStyleResource(defStyleRes)
.build()
.callTargetMethodsNow();
}
Це воно. Тепер, коли ви створюєте екземпляр класу з XML, супутник Spyglass інтерпретує атрибути та робить необхідний виклик методу. Наприклад, якщо ми надуємо наступний макет, то setTitle
він буде викликаний "Hello, World!"
як аргумент.
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:width="match_parent"
android:height="match_parent">
<com.example.CustomView
android:width="match_parent"
android:height="match_parent"
app:title="Hello, World!"/>
</FrameLayout>
Рамка не обмежується рядковими ресурсами, має багато різних приміток для обробки інших типів ресурсів. Він також має примітки для визначення значень за замовчуванням та для передачі значень заповнення, якщо ваші методи мають декілька параметрів.
Перегляньте репортаж Github для отримання додаткової інформації та прикладів.
android:title="@{"Hello, world!"}"
.
якщо ви опустите format
атрибут з attr
елемента, ви можете використовувати його для посилання на клас із макетів XML.
Refactor > Rename
працюєFind Usages
працюєне вказуйте format
атрибут у ... / src / main / res / values / attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
....
<attr name="give_me_a_class"/>
....
</declare-styleable>
</resources>
використовувати його в якомусь файлі макета ... / src / main / res / layout / activity__main_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<SomeLayout
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- make sure to use $ dollar signs for nested classes -->
<MyCustomView
app:give_me_a_class="class.type.name.Outer$Nested/>
<MyCustomView
app:give_me_a_class="class.type.name.AnotherClass/>
</SomeLayout>
проаналізуйте клас у коді ініціалізації перегляду ... / src / main / java /.../ MyCustomView.kt
class MyCustomView(
context:Context,
attrs:AttributeSet)
:View(context,attrs)
{
// parse XML attributes
....
private val giveMeAClass:SomeCustomInterface
init
{
context.theme.obtainStyledAttributes(attrs,R.styleable.ColorPreference,0,0).apply()
{
try
{
// very important to use the class loader from the passed-in context
giveMeAClass = context::class.java.classLoader!!
.loadClass(getString(R.styleable.MyCustomView_give_me_a_class))
.newInstance() // instantiate using 0-args constructor
.let {it as SomeCustomInterface}
}
finally
{
recycle()
}
}
}