我该如何使用XML声明一个Android UI元素?
我该如何使用XML声明一个Android UI元素?
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[]
资源,其中每个元素都是属性的ID。通过将属性名附加到元素名来生成XML中的每个属性。例如,R.styleable.MyCustomView_android_text
包含MyCustomView
的android_text
属性。然后可以使用各种get
函数从TypedArray
中检索属性。如果在XML中未定义该属性,则返回null
。当然,如果返回类型是基本类型,则返回第二个参数。
如果不想检索所有属性,则可以手动创建此数组。标准Android属性的ID包含在android.R.attr
中,而此项目的属性位于R.attr
中。
int attrsWanted[]=new int[]{android.R.attr.text, R.attr.textColor};
android.R.styleable
中的任何内容,因为它将来可能会更改。它仍然在文档中,因为查看所有这些常量的位置很有用。layout\main.xml
在顶级xml元素中包含命名空间声明xmlns:app="http://schemas.android.com/apk/res-auto"
。命名空间提供了一种方法,可以避免不同模式使用相同元素名称时发生的冲突(有关更多信息,请参见此文章)。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",则以下方式将无法工作(显示错误 " error: No resource identifier found for attribute 'newAttr' in package '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"
。请参见http://code.google.com/p/android/issues/detail?id=9656中的第57条评论。 - nmrres-auto
结尾,因为我们使用的是 Android Studio 和 Gradle。否则(例如某些 Eclipse 版本),它通常会以 lib/[your package name]
结尾。即 http://schemas.android.com/apk/lib/[your package name]
。 - Universe除了最受欢迎的答案之外,还有以下补充。
我想补充一些关于obtainStyledAttributes()用法的内容,当我们使用android:xxx预定义属性创建自定义视图时,特别是当我们使用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)
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>
同时,我们可以通过扩展标准UI组件来使用其声明的所有属性。 这种方法并不太好,因为例如TextView声明了很多属性。而且在重载onMeasure()和onDraw()时无法实现全部功能。
但是我们可以牺牲理论上广泛重用自定义组件的优点。说“我确切地知道我将使用哪些功能”,并且不与任何人共享代码。
然后,我们可以实现构造函数CustomComponent(Context, AttributeSet, defStyle)
。在调用super(...)
之后,我们将拥有所有解析的属性,并可以通过getter方法访问。
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>
这里的包含标签中的布局文件是同一 res 文件夹中的其他 .xml 布局文件。