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

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

Ответы [ 6 ]

828 голосов
/ 23 апреля 2010

В Руководстве разработчика по 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. Нестандартные атрибуты Android, такие как 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"
/> 

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

Образец Android 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"

Ссылки

90 голосов
/ 28 ноября 2011

Отличная ссылка. Спасибо! Дополнение к нему:

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

Если в библиотеке есть пакет "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" />
26 голосов
/ 28 февраля 2013

Дополнение к наиболее проголосовавшему ответу.

obtainStyledAttributes ()

Я хочу добавить несколько слов об использовании receiveStyledAttributes (), когда мы создаем пользовательское представление, используя атрибуты 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 для receiveStyledAttributes () очень плохая.

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

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

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

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

11 голосов
/ 29 июня 2012

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

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

5 голосов
/ 04 мая 2013

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

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

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

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

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

0 голосов
/ 04 февраля 2015

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

             <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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...