Оголошення користувацького елемента інтерфейсу Android з використанням XML


473

Як я можу оголосити елемент інтерфейсу Android за допомогою XML?


15
Якщо хтось шукає список підтримуваних, вбудованих форматів атрибутів, його можна знайти, тобто тут .
Marcin Orlowski

1
Хороший підручник для початку -> Створення складних переглядів на Android
Gayan Weerakutti

Відповіді:


840

Посібник для розробників 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"

Посилання


14
Я хотів би додати, що якщо ваш кореневий елемент вимагає вашого власного простору імен, вам доведеться додати як стандартний простір імен для android, так і власний користувальницький або інший, у вас можуть виникнути помилки побудови.
Чейз

11
Ця відповідь є найяснішим ресурсом в Інтернеті на користувальницьких параграфах XML, які мені вдалося знайти. Дякую, Casebash.
Артем Русаковський

2
чомусь візуальний редактор відмовляється використовувати написане значення тексту для android: text, але пристрій використовує його просто чудово. як так?
андроїд розробник

2
@androiddeveloper Схоже, редактор Eclipse відмовляється використовувати значення для всіх атрибутів android:. Я хотів би дізнатися, чи це функція чи помилка
deej

4
Яка мета xmlns: простір імен додатків та перезапуск авто?
ІгорГанапольський

91

Чудова довідка. Дякую! Доповнення до нього:

Якщо у вас є проект бібліотеки, який оголосив спеціальні атрибути для спеціального перегляду, ви повинні оголосити простір імен свого проекту, а не бібліотечного. Наприклад:

Зважаючи на те, що бібліотека має пакет "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" />

47
Це було зафіксовано в попередньому перегляді ADT 17. Щоб використовувати простір імен програми з бібліотеки, xmlns:app="http://schemas.android.com/apk/res-auto"
заявіть

2
Включаючи ваш власний простір імен, тепер повертається помилкаSuspicious namespace: Did you mean http://schemas.android.com/apk/res-auto
Бен Вілкінсон

користувацький простір імен закінчується в автоматичному режимі, оскільки ми використовуємо Android Studio і Gradle. В іншому випадку (наприклад, деякі версії Eclipse) це зазвичай закінчується в lib / [назва вашого пакета]
Всесвіт

користувацький простір імен закінчується, res-autoоскільки ми використовуємо Android Studio та Gradle. В іншому випадку (наприклад, деякі версії Eclipse) це, як правило, закінчується lib/[your package name]. тобтоhttp://schemas.android.com/apk/lib/[your package name]
Всесвіт

27

Доповнення до найбільш проголосованої відповіді.

getStyledAttributes ()

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

Розширення стандартного компонента інтерфейсу

У той же час ми можемо просто розширити стандартний компонент інтерфейсу, використовуючи всі його заявлені атрибути. Такий підхід не дуже хороший, оскільки, наприклад, TextView оголошує безліч властивостей. І неможливо реалізувати повну функціональність у переосмисленому onMeasure () та onDraw ().

Але ми можемо пожертвувати теоретично широким використанням користувацьких компонентів. Скажіть "Я точно знаю, які функції я буду використовувати", і не діліться кодом ні з ким.

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


роблять android: xxx наперед задані атрибути працюють у дизайнері gui eclipse?
deej

Такі атрибути розпізнаються плагіном Eclipse ADT в редакторі властивостей. Я можу побачити типові параметри зі свого стилю, якщо деякі значення не визначені. І не забудьте додати @RemoteView примітку до свого класу.
yuriy.weiss

Не можна змусити його працювати. Eclipse продовжує завантажувати нулі для getText та викидати android.content.res.Resources $ NotFoundException для getResourceId, хоча додаток працює на пристрої.
deej

Вибачте, я не можу тобі допомогти. Я створив лише демонстраційний проект для перевірки можливостей, а таких помилок не зустрічав.
yuriy.weiss

Це набагато краще, ніж відображення спеціальних атрибутів спеціального перегляду у вбудовані атрибути вбудованих представлень, що містяться в ньому.
саміс

13

Схоже, 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.


Я спробував це, проблема, з якою я маю, полягає в тому, що включений макет не може бути "адаптований", не може створювати генеричні дані. Наприклад, коли я включаю кнопку подібним чином, якщо я намагаюся встановити текст у xml, він працює.
cfl
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.