如何使用 XML 声明 Android UI 元素?
Android 开发人员指南中有一个名为 Building Custom Components 的部分。不幸的是,the discussion of XML attributes 只涉及在布局文件中声明控件,而不是实际处理类初始化中的值。步骤如下:
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
这样的非标准 android 属性需要声明其类型。在超类中声明的标签将在子类中可用,而无需重新声明。
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[]
资源,其中每个元素都是属性的 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
中的任何内容,因为根据 this thread,它可能会在未来发生变化。它仍然在文档中,因为在一个地方查看所有这些常量很有用。
3.在layout\main.xml等布局文件中使用
在顶级 xml 元素中包含命名空间声明 xmlns:app="http://schemas.android.com/apk/res-auto"
。命名空间提供了一种方法来避免不同架构使用相同元素名称时有时会发生的冲突(有关详细信息,请参阅 this article)。 URL 只是一种唯一标识模式的方式 - nothing actually needs to be hosted at that 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 标签视图示例
如果您想要一个完整的示例,请查看 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"
链接
StackOverflow 线程:检索自定义控件的 XML 属性
如何将obtainStyledAttributes与Android的内部主题一起使用
定义自定义属性 + 支持的属性格式列表
很好的参考。谢谢!对它的补充:
如果您碰巧包含了一个已为自定义视图声明自定义属性的库项目,则您必须声明您的项目名称空间,而不是库的名称空间。例如:
假设库有包“com.example.library.customview”并且工作项目有包“com.example.customview”,那么:
将不起作用(显示错误“错误:在包'com.example.library.customview'中找不到属性'newAttr'的资源标识符”):
<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"
请参阅 code.google.com/p/android/issues/detail?id=9656 中的评论 57
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]
除了投票最多的答案。
获得样式属性()
当我们使用 android:xxx prdefined 属性创建自定义视图时,我想添加一些关于obtainStyledAttributes() 用法的内容。特别是当我们使用 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>
也许,我在某些方面弄错了,但是有关 gainStyledAttributes() 的 Android 文档非常差。
扩展标准 UI 组件
同时,我们可以扩展标准 UI 组件,使用它声明的所有属性。这种方法不太好,因为例如 TextView 声明了很多属性。并且不可能在重写的 onMeasure() 和 onDraw() 中实现全部功能。
但是我们可以牺牲理论上对自定义组件的广泛重用。说“我确切地知道我将使用哪些功能”,并且不要与任何人共享代码。
然后我们可以实现构造函数CustomComponent(Context, AttributeSet, defStyle)
。调用 super(...)
后,我们将通过 getter 方法解析和使用所有属性。
非常感谢第一个答案。
至于我,我只有一个问题。当我放大我的视图时,我有一个错误: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>
这里包含标签中的布局文件是同一 res 文件夹中的其他 .xml 布局文件。