自定义view(creating a View Class)

来源:互联网 发布:求好玩的网络手机游戏 编辑:程序博客网 时间:2024/05/18 10:26

原文地址:http://docs.eoeandroid.com/training/custom-views/create-view.html

一个设计得好的自定义View跟其它设计得好的类一样:它提供了简易的接口,封装了便于在界面中使用的控件属性,以及能更有效的使用CPU和内存,等等。另外,作为一个设计良好的自定义View,它应包含如下几点:

a)、应满足android原生控件的设计标准,或者说保持一致性;

b)、提供可以在xml文件中设置的自定义属性;

c)、发送可访问的事件;

d)、兼容多个android平台

android framework提供了一套基类与xml标签来帮助我们实现需自定义的view。这里我们就来学习如何用android framework所提供基类及xml标签创建一个自定义的view。

View的子类

android定义的所有控件类都是继承自基类View.java。我们的自定义view可以直接继承View,也可以继承一些已经存在的其它控件,如Button等。

为了便于在ADT中使用我们的自定义view,自定义view至少必须提供一个同时包含参数Context和AttributeSet的构造函数,此构造函数便于布局编辑器实例化我们的自定义view及进行编辑。构造函数如下:

class PieChart extends View {    public PieChart(Context ctx, AttributeSet attrs) {        super(ctx, attrs);    }}
定义自定义属性

在用户界面中添加一个原生的view时,我们可以通过在xml中通过设置它的属性来控制此控件的显示样式和行为等。自定义view同样也可以通过这种方式来添加和控制它的属性,为了实现与原生控件同样的功能,我们必须实现:

a)、在资源属性中使用<declare-styleable></declare-styleable>标签为我们的view定义自定义属性;

b)、指定xml中将要用的属性的具体的值;

c)、在代码中动态的检测属性值;

d)、将检测到的属性值设置给我们的自定义View;

此部分我们讨论如何定义自定义属性以及指定属性具体的值。下一部分我们将讨论如何在代码中检测属性值并将属性值设置给自定义的view。

定义自定义属性,需要在我们的工程中添加<declare-styleable>资源。通常我们将这些资源放在res\values\attrs.xml中,下面是一个attrs.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>
上面的代码定义了两个自定义的属性,showText和labelPosition,他们属于一个叫PieChart的样式实体。样式实体的名称一般应保持与自定义view的类名保持一致,当然你也可以不遵循这种原则,但是一般比较好的代码都用这种方法来申明。

一旦你定义了自定义的属性,你就可以想运用原生的控件属性一样在xml中使用它。唯一不同的是,自定义的属性与原生控件属性的命名空间不同,原生控件属性的命名空间是:http://schemas.android.com/apk/res/android而自定义属性的命名空间是:http://schemas.android.com/apk/res/[your package name]。举个例子,如下是如何运用为PieChart定义的自定义属性:

<?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>
为了避免重复这个巨长的uri,本示例用了xmlns指示。这个指示将命名空间“http://schemas.android.com/apk/res/com.example.customviews”叫做custom,当然你可以给你的命名空间起任何你喜欢的别名。

注意你在布局中用来添加自定义控件的标签(<com.example.customviews.charting.PieChart />),它必须是完整的包名加类名。如果你的自定义view是一个内部类的话,只指定到它的外部类是不行的,必须准确的指定到内部类。例如,若PieChart还有一个自定义view的内部类PieView,在使用时,标签名必须是这样的<com.example.customviews.charting.PieChart$PieView />。

申请自定义属性

当一个view在布局文件中创建出来以后,xml标签中的所有属性都会从资源文件包中读取出来并被放到[AttributeSet]中传入到这个view的构造函数中。尽管它可以从[AttributeSet]中直接读取,但是这样做有几点不好的地方:

a)、携带属性值的资源引用是没有被处理过的;

b)、样式并没有申请过;

然而,将AttributeSet传给obtainStyleAttributes(),后者将返回一个含有处理过的引用和样式的TypedArray。

为了简单的调用obtainStyleAttributes()方法,android资源编译器做了大量的工作。对于res文件夹下的每一个<declare-styleable />资源,自动生成的R.java文件中都定义了一个属性id的数组以及一套指向数组中id的常量。后面就可以使用预先定义好的常量从TypedArray中读取属性。下面的例子是PieChart如何读取它的属性:

public PieChart(Context ctx, AttributeSet attrs) {   super(ctx, 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();   }}
注意TypedArray对象是一个共享的资源,使用完后必须进行回收。


添加属性和事件

属性是控制view的行为和外观的强有力方式,但是他们只有在被实例化初始化之后才能读取这些属性。为了提供动态的行为,必须向外暴露每个属性的getter()和setter()方法。下面的代码片段展示了如何向外暴露PieChart的showText属性:

public boolean isShowText() {   return mShowText;}public void setShowText(boolean showText) {   mShowText = showText;   invalidate();   requestLayout();}
注意setShowText(boolean showText)方法调用了invalidate()和requestLayout()方法,为了保证view的行为是实时可靠这两个方法的调用非常重要。任何有可能改变view的外观属性变动之后都需要invalidate(),以便系统知道需要重新绘制这个view。同样的,当一个属性有可能改变这个view的尺寸或形状时,你需要重新布局(requestLayout())。忘记调用这两个方法将会导致很难发现的bug。

自定义view同样应该提供重要事件的监听。例如,PieChart暴露了一个叫做OnCurrentItemChanged的监听器,用来监听用户旋转了饼图将焦点放在了饼图的另一部分。

忘记向外暴露属性和监听是非常容易发生的事情,特别是这个自定义view只有你自己用的时候。花点时间来考虑你的自定义view的接口,一遍减少重构。有个好的规则可以遵循,就是经常向外提供任何一个有可能影响自定义view的外观和行为的属性。

无障碍设计

你的自定义view必须支持广大的用户。


0 0