Объявление пользовательского элемента пользовательского интерфейса Android с использованием XML

450

Как объявить элемент Android UI с помощью XML?

Теги:
user-interface

6 ответов

789
Лучший ответ

В Руководстве разработчика Android есть раздел Построение пользовательских компонентов. К сожалению, обсуждение атрибутов XML охватывает только объявление элемента управления внутри файла макета, а не обработку значений внутри инициализации класса. Эти шаги заключаются в следующем:

1. Объявлять атрибуты в values\attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyCustomView">
        <attr name="android:text"/>
        <attr name="android:textColor"/>            
        <attr name="extraInformation" format="string" />
    </declare-styleable>
</resources>

Обратите внимание на использование неквалифицированного имени в теге declare-styleable. Нестандартные атрибуты андроида, такие как extraInformation, должны иметь тип объявления. Теги, объявленные в суперклассе, будут доступны в подклассах без необходимости их обновления.

2. Создание конструкторов

Поскольку существует два конструктора, которые используют инициализацию AttributeSet, удобно создать отдельный метод инициализации для вызываемых конструкторов.

private void init(AttributeSet attrs) { 
    TypedArray a=getContext().obtainStyledAttributes(
         attrs,
         R.styleable.MyCustomView);

    //Use a
    Log.i("test",a.getString(
         R.styleable.MyCustomView_android_text));
    Log.i("test",""+a.getColor(
         R.styleable.MyCustomView_android_textColor, Color.BLACK));
    Log.i("test",a.getString(
         R.styleable.MyCustomView_extraInformation));

    //Don't forget this
    a.recycle();
}

R.styleable.MyCustomView - это автогенерированный ресурс int[], где каждый элемент является идентификатором атрибута. Атрибуты генерируются для каждого свойства в XML, добавляя имя атрибута к имени элемента. Например, R.styleable.MyCustomView_android_text содержит атрибут android_text для MyCustomView. Атрибуты затем могут быть извлечены из TypedArray с использованием различных функций get. Если атрибут не определен в определенном в XML, возвращается null. За исключением, конечно, если возвращаемый тип является примитивным, в этом случае возвращается второй аргумент.

Если вы не хотите извлекать все атрибуты, можно создать этот массив вручную. Идентификатор стандартных атрибутов Android включен в android.R.attr, тогда как атрибуты для этого проекта находятся в R.attr.

int attrsWanted[]=new int[]{android.R.attr.text, R.attr.textColor};

Обратите внимание, что вы должны не использовать что-либо в android.R.styleable, в соответствии с этот поток он может изменяться в будущее. В документации все еще есть просмотр всех этих констант в одном месте.

3. Используйте его в файлах макетов, таких как layout\main.xml

Включить объявление пространства имен xmlns:app="http://schemas.android.com/apk/res-auto" в элемент xml верхнего уровня. Пространства имен предоставляют метод, позволяющий избежать конфликтов, которые иногда возникают, когда разные схемы используют одни и те же имена элементов (см. эту статью для получения дополнительной информации). URL-адрес - это просто способ однозначной идентификации схем - ничего не нужно размещать на этом URL. Если это ничего не делает, это связано с тем, что вам действительно не нужно добавлять префикс пространства имен, если только вам не нужно разрешать конфликт.

<com.mycompany.projectname.MyCustomView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@android:color/transparent"
    android:text="Test text"
    android:textColor="#FFFFFF"
    app:extraInformation="My extra information"
/> 

Ссылка на пользовательский вид с использованием полного имени.

Пример LabelView для Android

Если вы хотите получить полный пример, посмотрите на образец представления метки андроида.

LabelView.java

 TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
 CharSequences=a.getString(R.styleable.LabelView_text);

attrs.xml

<declare-styleable name="LabelView">
    <attr name="text"format="string"/>
    <attr name="textColor"format="color"/>
    <attr name="textSize"format="dimension"/>
</declare-styleable>

custom_view_1.xml

<com.example.android.apis.view.LabelView
    android:background="@drawable/blue"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    app:text="Blue" app:textSize="20dp"/>

Это содержится в LinearLayout с атрибутом пространства имен: xmlns:app="http://schemas.android.com/apk/res-auto"

Ссылки

  • 13
    Я хотел бы добавить, что если вашему корневому элементу требуется ваше собственное пространство имен, вам придется добавить как стандартное пространство имен Android, так и свое собственное, иначе вы можете столкнуться с ошибками сборки.
  • 10
    Этот ответ - самый понятный ресурс в Интернете по пользовательским параметрам XML, который мне удалось найти. Спасибо, Касебаш.
Показать ещё 10 комментариев
92

Отличная ссылка. Благодарю! Кроме того:

Если у вас есть проект библиотеки, который объявил пользовательские атрибуты для пользовательского представления, вы должны объявить пространство имен проектов, а не его библиотеку. Например:

Учитывая, что в библиотеке есть пакет "com.example.library.customview", а рабочий проект имеет пакет "com.example.customview", то:

Ошибка работы (показывает ошибку): идентификатор ресурса не найден для атрибута 'newAttr' в пакете    'com.example.library.customview' "):

<com.library.CustomView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/com.example.library.customview"
        android:id="@+id/myView"
        app:newAttr="value" />

Будет работать:

<com.library.CustomView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/com.example.customview"
        android:id="@+id/myView"
        app:newAttr="value" />
  • 46
    Это было исправлено в предварительном просмотре ADT 17. Чтобы использовать пространство имен приложения из библиотеки, объявите xmlns:app="http://schemas.android.com/apk/res-auto" См. Комментарий 57 в code.google.com/p/android/issues/detail?id= 9656
  • 2
    Включение вашего собственного пространства имен теперь возвращает ошибку Suspicious namespace: Did you mean http://schemas.android.com/apk/res-auto
Показать ещё 2 комментария
27

Дополнение к большинству голосующих ответов.

obtainStyledAttributes()

Я хочу добавить несколько слов об использовании getStyledAttributes() при создании пользовательского представления с использованием атрибутов android: xxx prdefined. Особенно, когда мы используем TextAppearance.
Как упоминалось в "2. Создание конструкторов", пользовательское представление получает AttributeSet при его создании. Основное использование мы можем увидеть в исходном коде TextView (API 16).

final Resources.Theme theme = context.getTheme();

// TextAppearance is inspected first, but let observe it later

TypedArray a = theme.obtainStyledAttributes(
            attrs, com.android.internal.R.styleable.TextView, defStyle, 0);

int n = a.getIndexCount();
for (int i = 0; i < n; i++) 
{
    int attr = a.getIndex(i);
    // huge switch with pattern value=a.getXXX(attr) <=> a.getXXX(a.getIndex(i))
}
a.recycle();

Что мы можем увидеть здесь? obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
Набор атрибутов обрабатывается темой в соответствии с документацией. Значения атрибутов скомпилируются поэтапно. Первые атрибуты заполняются из темы, затем значения заменяются значениями из стиля, и, наконец, точные значения из XML для специального экземпляра вида заменяют другие.
Массив запрашиваемых атрибутов - com.android.internal.R.styleable.TextView
Это обычный массив констант. Если мы запрашиваем стандартные атрибуты, мы можем создать этот массив вручную.

Что не упоминается в документации - порядок результата. Элементы TypedArray.
Когда пользовательский вид объявляется в attrs.xml, генерируются специальные константы для индексов атрибутов. И мы можем извлечь значения таким образом: a.getString(R.styleable.MyCustomView_android_text). Но для руководства int[] нет констант. Я полагаю, что getXXXValue (arrayIndex) будет работать нормально.

И другой вопрос: "Как мы можем заменить внутренние константы и запрашивать стандартные атрибуты?" Мы можем использовать значения android.R.attr. *.

Итак, если мы хотим использовать стандартный атрибут TextAppearance в пользовательском представлении и читать его значения в конструкторе, мы можем изменить код из TextView таким образом:

ColorStateList textColorApp = null;
int textSize = 15;
int typefaceIndex = -1;
int styleIndex = -1;

Resources.Theme theme = context.getTheme();

TypedArray a = theme.obtainStyledAttributes(attrs, R.styleable.CustomLabel, defStyle, 0);
TypedArray appearance = null;
int apResourceId = a.getResourceId(R.styleable.CustomLabel_android_textAppearance, -1);
a.recycle();
if (apResourceId != -1)
{
    appearance = 
        theme.obtainStyledAttributes(apResourceId, new int[] { android.R.attr.textColor, android.R.attr.textSize, 
            android.R.attr.typeface, android.R.attr.textStyle });
}
if (appearance != null)
{
    textColorApp = appearance.getColorStateList(0);
    textSize = appearance.getDimensionPixelSize(1, textSize);
    typefaceIndex = appearance.getInt(2, -1);
    styleIndex = appearance.getInt(3, -1);

    appearance.recycle();
}

Если определен CustomLabel:

<declare-styleable name="CustomLabel">
    <!-- Label text. -->
    <attr name="android:text" />
    <!-- Label text color. -->
    <attr name="android:textColor" />
    <!-- Combined text appearance properties. -->
    <attr name="android:textAppearance" />
</declare-styleable>

Возможно, я ошибаюсь, но документация на Android по getStyledAttributes() очень бедна.

Расширение стандартного компонента пользовательского интерфейса

В то же время мы можем просто расширить стандартный компонент пользовательского интерфейса, используя все его объявленные атрибуты. Этот подход не так хорош, потому что TextView, например, объявляет много свойств. И это будет невозможно для реализации полной функциональности в overrideen onMeasure() и onDraw().

Но мы можем пожертвовать теоретическим широким повторным использованием пользовательского компонента. Скажем: "Я точно знаю, какие функции я буду использовать", и не передавайте код ни с кем.

Тогда мы можем реализовать конструктор CustomComponent(Context, AttributeSet, defStyle). После вызова super(...) мы получим все атрибуты, проанализированные и доступные с помощью методов getter.

  • 0
    ли android: предопределенные атрибуты xxx работают в eclipse gui designer?
  • 0
    Такие атрибуты распознаются плагином Eclipse ADT в редакторе свойств. Я могу видеть значения по умолчанию из моего стиля, если некоторые значения не определены. И не забудьте добавить аннотацию @RemoteView в ваш класс.
Показать ещё 3 комментария
10

Кажется, Google обновил страницу своего разработчика и добавил там различные тренинги.

Один из них касается создания пользовательских представлений и может быть найден здесь

5

Большое спасибо за первый ответ.

Что касается меня, у меня была только одна проблема. При раздувании моего взгляда у меня была ошибка: java.lang.NoSuchMethodException: MyView (контекст, атрибуты)

Я решил это, создав новый конструктор:

public MyView(Context context, AttributeSet attrs) {
     super(context, attrs);
     // some code
}

Надеюсь, это поможет!

0

Вы можете включить любой файл макета в другой файл макета как -

             <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="30dp" >

                <include
                    android:id="@+id/frnd_img_file"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    layout="@layout/include_imagefile"/>

                <include
                    android:id="@+id/frnd_video_file"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    layout="@layout/include_video_lay" />

                <ImageView
                    android:id="@+id/downloadbtn"
                    android:layout_width="30dp"
                    android:layout_height="30dp"
                    android:layout_centerInParent="true"
                    android:src="@drawable/plus"/>
            </RelativeLayout>

здесь файлы макета в теге include - это другие файлы макета .xml в той же папке res.

  • 0
    Я пробовал это, проблема, с которой я столкнулся, заключается в том, что включенный макет не может быть «адаптирован», не может создавать универсальные шаблоны. Например, когда я включаю кнопку аналогичным образом, если я пытаюсь установить текст в xml, он работает.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню