Определение пользовательских привлечений
-
27-09-2019 - |
Вопрос
Мне нужно реализовать свои собственные атрибуты, как в com.android.R.attr
Ничего не нашел в официальной документации, поэтому мне нужна информация о том, как определить эти привлечения и как их использовать из моего кода.
Решение
В настоящее время наилучшая документация является источником. Вы можете взглянуть на это здесь (attris.xml).
Вы можете определить атрибуты в верхней части <resources>
элемент или внутри <declare-styleable>
элемент. Если я собираюсь использовать Attr в более чем на одном месте, я положил его в корневой элемент. Примечание, все атрибуты имеют одно и то же глобальное пространство имен. Это означает, что даже если вы создаете новый атрибут внутри <declare-styleable>
Элемент его можно использовать вне его, и вы не можете создать другой атрибут с тем же именем другого типа.
Ан <attr>
Элемент имеет два атрибута XML name
а также format
. name
позвольте вам назвать это чем-то, и это то, как вы в конечном итоге ссылаетесь на него в коде, например, R.attr.my_attribute
. Отказ То format
Атрибут может иметь разные значения в зависимости от того, что вы хотите «тип» атрибута.
- Ссылка - если он ссылается на другой идентификатор ресурса (например, @ color / my_color "," @ layout / my_layout ")
- цвет
- логический
- измерение
- плавать
- целое число
- нить
- доля
- enum - нормально неявно определен
- Флаг - обычно неявно определен
Вы можете установить формат для нескольких типов, используя |
, например, 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 ATTR, например, 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();
}
Конец. :)
Другие советы
Ответ 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. Вот кодовый фрагмент из моего SeekbarPreferferferference, который устанавливает резюме предпочтения форматирования его значения в соответствии со значением предпочтения. Если XML для наборов предпочтений Android: Summary к текстовой строке или строке возобновляется, значение предпочтения отформатировано в строку (она должна иметь% 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 Framework. Отказ Чтобы продемонстрировать, как это работает, вот пример, показывающий, как сделать пользовательский вид, который отображает заголовок строки.
Шаг 1: Создайте пользовательский класс View.
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 Framework для маркировки значения атрибута к этому методу, когда надувается вид.
@HandlesString(attributeId = R.styleable.CustomView_title)
public void setTitle(String title) {
titleView.setText(title);
}
Теперь, когда ваш класс имеет аннотацию SPYGLASS, SPYGLASS Framework обнаружит ее при компиляционном времени и автоматически генерирует 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>
Рамки не ограничиваются строковыми ресурсами, имеет множество различных аннотаций для обработки других типов ресурсов. Он также имеет аннотации для определения значений по умолчанию и для передачи значений заполнителей, если ваши методы имеют несколько параметров.
Посмотрите на REPO GitHUB для получения дополнительной информации и примеров.
Если вы опускаете format
атрибут из attr
Элемент, вы можете использовать его для ссылки класса от макетов XML.
- пример от Abtri.xml..
- Android Studio понимает, что класс ссылается от XML
- т.е.
Refactor > Rename
работаетFind Usages
работает- и так далее...
- т.е.
Не указывайте а format
атрибут In ... / Src / Main / Res / Value / attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
....
<attr name="give_me_a_class"/>
....
</declare-styleable>
</resources>
Используйте его в некотором файле макета ... / SRC / Главная / 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()
}
}
}