使用XML声明一个自定义android UI元素

我如何声明一个Android UI元素使用XML?

202649 次浏览

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标记中使用了一个非限定名称。像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包含MyCustomViewandroid_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中的任何东西,因为根据这个线程,它在未来可能会改变。它仍然在文档中被认为在一个地方查看所有这些常量是有用的。

3.在诸如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 LabelView样本

如果你想要一个完整的例子,看看android标签视图示例。

LabelView.java

 TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
CharSequences=a.getString(R.styleable.LabelView_text);

attrs.xml .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 .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"

链接

< p >很好的参考。谢谢! 对它的补充:

如果您碰巧包含一个库项目,该库项目为自定义视图声明了自定义属性,那么您必须声明您的项目名称空间,而不是库的名称空间。例如:

假定该库有包"com.example.library. library "。Customview”,工作项目有包“com.example”。customview”,那么:

将不工作(显示错误" error:在包中没有为属性'newAttr'找到资源标识符 “com.example.library。customview”):< / p >

<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" />

谷歌似乎更新了它的开发者页面,并在那里增加了各种培训。

其中一个处理自定义视图的创建,可以在在这里中找到

除了投票最多的答案。

obtainStyledAttributes ()

我想添加一些关于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();

我们在这里可以看到什么?< br > obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) < br > 属性集根据文档按主题处理。属性值是逐步编译的。首先从主题填充属性,然后用样式中的值替换值,最后用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文档obtainStyledAttributes()是非常差的。

扩展标准UI组件

同时,我们可以扩展标准的UI组件,使用它所有声明的属性。 这种方法不是很好,因为TextView for instance声明了很多属性。这是不可能的 在onMeasure()和onDraw()中实现完整的功能 但是我们可以牺牲自定义组件理论上的广泛重用。说“我确切地知道我将使用什么功能”,然后

然后我们可以实现构造函数CustomComponent(Context, AttributeSet, defStyle)。 调用super(...)后,我们将解析所有属性,并通过getter方法可用

非常感谢你的第一个回答。

至于我,我只有一个问题。当我夸大我的观点时,我有一个bug: 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标签中的布局文件是同一res文件夹中的其他.xml布局文件。