Creating a View class
一个精心设计的自定以View就好比其他精心设计的Class。它封装一组特定并且容易使用的接口,充分利用cpu和内存,等等。除了是一个精心设计的类,一个定制的View应该:
---符合Android标准
---提供自定义属性为xml布局服务
---发送可访问事件
---兼容Android多平台
SubClass a View
在Frameworks 中定义的所有视图类都是继承与View,你的自定义View可以继承View,或者节约时间继承其他视图类,比如Button
你必须至少提供Context和AttrbuteSet作为参数的构造函数,目的是为了ADT与自定义View交互。这个构造函数允许视图编辑器创建和编辑你的View实例。
class PieChart extends View {
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
Define Custom Attributes
为了添加内置View到用户界面,你应该指定该View到XML布局文件中,通过属性来控制View的显示和行为。良好的自定义View还能添加到XML和样式化。
要实现这样的自定义View,你必须:
---在资源文件中用 <declare-styleable>
为你的View定义属性
---在你的XML布局文件中指定属性的值
---运行时检索属性的值
---将检索出来的属性值应用到你的View
这个节点讨论怎么定义自定义属性和指定自定义属性的值。下个节点讨论检索属性值以及应用属性值。
增加<declare-styleable>资源到你的Project来定义自定义属性。一般做法是将这些资源放到res/values/attr.xml文件中。例子如下:
<resources>
<declare-styleable name="PieChart">
<attr name="showText" format="boolean" />
<attr name="labelPosition" format="enum">
<enum name="left" value="0"/>
<enum name="right" value="1"/>
</attr>
</declare-styleable>
</resources>
这段代码定义了属于PieChart实体节点的showText和labelosition两个属性,按照惯例<declare-styleable>实体节点命名和自定义View的Class命名相同。
虽然没有必要严格遵守这个惯例,很多流行的代码编辑器取决于该命名惯例来完成语法。
一旦你成功定义属性,就能在XML布局文件中像Android内置属性一样使用他们。唯一的不同就是自定义属性属于不同的名称空间,而不是属于
http://schemas.android.com/apk/res/android
名称空间,而是属于http://schemas.android.com/apk/res/[your package name]。
例如,piehart实体节点定义的属性使用范例如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews">
<com.example.customviews.charting.PieChart
custom:showText="true"
custom:labelPosition="left" />
</LinearLayout>
为了避免重复使用这么长的名称空间标识符,请参照上面的xmlns节点写法。具体做法是给名称空间
http://schemas.android.com/apk/res/com.example.customviews
指定一个别名,即custom。这样你就能选择所需要的名称空间别名。
PS:添加自定义View到XML布局文件中时,必须加上Class的全路径,如果Class是一个内部类还的加上$标识符。
比如PieChart中有一个叫PieView的内部类,要使用这个类的自定义属性,的这样指定:
com.example.customviews.charting.PieChart$PieView
Apply Custom Attributes
当XML布局文件中的View被创建时,在xml标签中定义的所有属性从资源包中读取,以AttributeSet对象的方式传到自定义View的构造函数中。
虽然可以从AttributeSet中直接读取值,但是有这样做有如下缺点:
---资源引用中的属性值没有被解决(没整明白)
---样式不能被应用
代替方法是将AttributeSet传到obtainStyledAttributes(),该方法会返回一个已经被引用和样式化的TypedArray数组。
Android资源编译器通过调用obtainStyledAttributes()可以为你更容易的做更多工作。
在res下的每一个<declare-styleable>资源生成R.java定义了一个属性ids的数组和常量集合,即为数组中的每个属性定义一个索引。
你使用预先定义的常量从TypedArray中读取属性,这里是PieChart类读取属性的范例:
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.PieChart,
0, 0);
try {
mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
} finally {
a.recycle();
}
}
PS:TypedArray是一个共享资源,用完了记得释放
Add Properties and Events
属性集合是控制View显示和行为的一个强有力方式,但是他们只能在View初始化的时候被读取。
为每一个属性暴露get()和set()来动态改变View的行为。下面的代码片段向你展示PieChart怎样暴露showText属性:
public boolean isShowText() {
return mShowText;
}
public void setShowText(boolean showText) {
mShowText = showText;
invalidate();
requestLayout();
}
PS:setShowText()调用invalidate()和requestLayout()是保证View正确展现,这至关重要。任何属性改变导致的View外观变化都必须使View无效,
这样系统才能知道该View需要被重新绘制,即invalidate()。同样的,任何属性改变导致View尺寸和边框变化后必须请求一个新的布局,即requestLayout()。
如果你忘掉这些可能出现比较难发现的Bug。
Design For Accessibility
没整明白这个
自定义View样式这篇文章讲解的非常详细,推荐之