Мне нужно реализовать свои собственные атрибуты, как в com.android.R.attr
Ничего не нашел в официальной документации, поэтому мне нужна информация о том, как определить эти атрибуты и как использовать их в моем коде.
Мне нужно реализовать свои собственные атрибуты, как в com.android.R.attr
Ничего не нашел в официальной документации, поэтому мне нужна информация о том, как определить эти атрибуты и как использовать их в моем коде.
На данный момент лучшая документация - это источник. Вы можете ознакомиться с ним здесь (attrs. xml).
Вы можете определить атрибуты в верхнем <resources>
элементе или внутри <declare-styleable>
элемента. Если я собираюсь использовать attr более чем в одном месте, я помещаю его в корневой элемент. Обратите внимание, что все атрибуты используют одно и то же глобальное пространство имен. Это означает, что даже если вы создаете новый атрибут внутри элемента <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
. Если вы хотите повторно использовать атрибут 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/
- person CommonsWare; 09.08.2010
xmlns:my="http://schemas.android.com/apk/lib/my.namespace"
- нет копирования attrs.xml . Обратите внимание, что путь URI пространства имен должен быть / apk / * lib *, а не / apk / res.
- person thom_nic; 01.03.2012
<attr name="title" format="string"/>
существует в пакете com.pack1
(в module1
), а также тот же атрибут существует в com.pack2
(в module2
), и оба модуля указаны в module3
(исполняемый файл). Я получил ошибку Attribute "title" has already been defined
.
- person anonim; 28.08.2012
format
и их значения нигде не описаны в официальных документах? Я хорошо посмотрел и не могу их найти. Кажется странным для такой важной особенности архитектуры.
- person JulianSymes; 01.12.2012
apk/lib
не сработал для меня с настраиваемыми атрибутами со справочным форматом из проекта библиотеки. Что сделал, так это использование apk/res-auto
, как предложено в stackoverflow.com/a/13420366/22904 чуть ниже, а также в stackoverflow.com/a/10217752
- person Giulio Piancastelli; 14.01.2013
drawable
или values
xml файлах?
- person taylor; 24.04.2013
enum
и flag
: первое позволяет нам выбрать одно и только одно значение, второе позволяет нам комбинировать несколько. Я написал более подробный ответ на аналогичный вопрос здесь, и теперь нашел этот вопрос Я решил, что на это свяжусь.
- person Rad Haring; 13.03.2014
xmlns:xxxx="http://schemas.android.com/apk/res/my_package_name"
и он работает
- person suitianshi; 22.07.2014
a.recycle()
здесь очень важен для освобождения памяти
- person Tash Pemhiwa; 21.01.2016
MyCustomView(Context context, AttributeSet attrs, int defStyle)
не вызывался, поэтому изменение на MyCustomView(Context context, AttributeSet attrs)
сработало, и я использовал context.obtainStyledAttributes(attrs,R.styleable.MyCustomView)
, чтобы получить TypedArray
.
- person Zeeshan Ghazanfar; 27.09.2016
width
и height
работают со значениями типа 16dp
и match_parent
? Они dimension|enum
?
- person Michel Feinstein; 03.09.2018
Ответ 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.
Во-вторых, он не описывает, как работать с ресурсами plaurals (количественными строками). С ними нельзя справиться с помощью TypedArray. Вот фрагмент кода из моего SeekBarPreference, который устанавливает сводку предпочтения, форматируя его значение в соответствии со значением предпочтения. Если xml для предпочтения устанавливает android: summary в текстовую строку или строку 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: Определите строковый атрибут в 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!"}"
.
- person Spook; 04.02.2019
если вы опустите атрибут format
в элементе attr
, вы можете использовать его для ссылки на класс из макетов XML.
Refactor > Rename
worksFind 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()
}
}
}