Как объявить элемент пользовательского интерфейса Android с помощью XML?
Как объявить элемент пользовательского интерфейса Android с помощью XML?
Ответы:
В Руководстве для разработчиков Android есть раздел под названием « Создание пользовательских компонентов» . К несчастью, обсуждение атрибутов XML охватывает только объявление элемента управления в файле макета, а не обработку значений внутри инициализации класса. Шаги следующие:
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
должны иметь объявленный тип. Теги, объявленные в суперклассе, будут доступны в подклассах без необходимости повторного объявления.
Поскольку есть два конструктора, которые используют 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
соответствии с этой темой это может измениться в будущем. Это все еще в документации, так как полезно просматривать все эти константы в одном месте.
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.
TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
CharSequences=a.getString(R.styleable.LabelView_text);
<declare-styleable name="LabelView">
<attr name="text"format="string"/>
<attr name="textColor"format="color"/>
<attr name="textSize"format="dimension"/>
</declare-styleable>
<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"
Отличная ссылка. Спасибо! Дополнение к нему:
Если у вас есть библиотечный проект, в котором объявлены настраиваемые атрибуты для настраиваемого представления, вы должны объявить пространство имен вашего проекта, а не библиотечное. Например:
Учитывая, что в библиотеке есть пакет «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" />
xmlns:app="http://schemas.android.com/apk/res-auto"
Смотрите комментарий 57 в code.google.com/p/android/issues/detail?id=9656
Suspicious namespace: Did you mean http://schemas.android.com/apk/res-auto
res-auto
потому что мы используем Android Studio и Gradle. В противном случае (например, некоторые версии Eclipse) он обычно заканчивается на lib/[your package name]
. то естьhttp://schemas.android.com/apk/lib/[your package name]
Дополнение к наиболее голосуемому ответу.
Я хочу добавить несколько слов об использовании 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(...)
все атрибуты будут проанализированы и доступны через методы получения.
Похоже, что Google обновил свою страницу для разработчиков и добавил туда различные тренинги.
Один из них посвящен созданию пользовательских видов и может быть найден здесь
Большое спасибо за первый ответ.
Что касается меня, у меня была только одна проблема с этим. Раздувая мой взгляд, у меня была ошибка: java.lang.NoSuchMethodException: MyView (Context, Attributes)
Я решил это, создав новый конструктор:
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// some code
}
Надеюсь, это поможет!
Вы можете включить любой файл макета в другой файл макета, как
<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.