使用XML声明自定义的Android UI元素
如何使用XML声明Android UI元素?
Android开发者指南有一个名为“ build立自定义组件”的部分 。 不幸的是, 对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
需要声明其types。 在超类中声明的标签将在子类中可用,而不必重新声明。
2.创build构造函数
由于有两个构造函数使用AttributeSet
进行初始化,因此为构造函数创build一个单独的初始化方法以方便调用。
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
。 当然,除了返回types是一个原语,在这种情况下,返回第二个参数。
如果您不想检索所有属性,可以手动创build此数组。标准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只是一种唯一标识模式的方式 – 实际上并不需要托pipe在该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"
链接
- StackOverflow线程:检索自定义控件的XML属性
- 如何使用Android的内部主题的getsStyledAttributes
- 定义自定义属性+支持的属性格式列表
伟大的参考。 谢谢! 除此之外:
如果你碰巧有一个包含已经声明自定义视图的自定义属性的库项目,你必须声明你的项目命名空间,而不是库的。 例如:
鉴于库具有“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" />
除了大多数投票答案。
obtainStyledAttributes()
当我们使用android:xxx prdefined属性创build自定义视图时,我想添加一些关于getsStyledAttributes()的用法。 特别是当我们使用TextAppearance。
正如在“2.创build构造函数”中提到的,自定义视图在创build时获得了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)
属性集根据文档由主题处理。 属性值是逐步编译的。 首先从主题填充属性,然后用样式中的值replace值,最后从XML中为特殊视图实例replace其他值。
请求的属性数组 – com.android.internal.R.styleable.TextView
这是一个普通的常量数组。 如果我们要求标准属性,我们可以手动build立这个数组。
什么没有在文档中提到 – 结果的顺序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>
也许,我误解了一些方法,但在getsStyledAttributes()上的Android文档非常差。
扩展标准的UI组件
同时,我们可以扩展标准的UI组件,使用它所有的声明属性。 这种方法不太好,因为TextView实例声明了很多属性。 在overriden onMeasure()和onDraw()中实现完整的function将是不可能的。
但是我们可以牺牲理论上广泛的定制组件的复用。 说“我确切知道我将使用哪些function”,并且不要与任何人共享代码。
然后我们可以实现构造函数CustomComponent(Context, AttributeSet, defStyle)
。 在调用super(...)
我们将通过getter方法parsing和使用所有属性。
Google似乎更新了开发者页面,并在那里添加了各种培训。
其中之一处理创build自定义视图,可以在这里find
非常感谢第一个答案。
至于我,我只有一个问题。 当我膨胀我的看法,我有一个bug: java.lang.NoSuchMethodException:MyView(上下文,属性)
我通过创build一个新的构造函数来解决它:
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布局文件。