Вопрос

Мне нужно реализовать свои собственные атрибуты, как в 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()
            }
        }
    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top