自定义控件(一)

        最近写个小项目,因为所谓的版权问题头疼的不行,比较烦躁。于是乎,想写点东西,平静平静。也正好弥补下我那不扎实的基础。前段时间看网上的一些有关自定义控件的文章,感觉有的虽然Demo写的很详细,并且也学会了。但是最后还是会有人有疑问:什么是自定义控件?或者有人问:你会自定义控件么? 这问题给我的感觉就是,你会做饭么?更有些初学者不知道attrs.xml是干什么的,不明白为什么View要有三个构造方法。好了,从基础开始吧。人心已经如此的浮躁,我们可不能再浮躁了。脚踏实地,从基础开始。Ok,废话少说,言归正传。

        这篇文章介绍自定义控件,一篇肯定搞不定,打算写一个系列。很显然,学Android最直接的地方就是Google的官方文档了。什么?英文不好,看不懂?好吧,我就用我的蹩脚英语来帮帮你,但你可千万别指望我这百分百正确。打开官网,在Develop->Training->Best Practices For User Interface->Creating Custom Views下,里面有四个子版块用来讲解创建自定义控件,并且提供了一个完整的Demo(点我下载)。这个Demo运行起来是这个样子滴。

        看起来还不错,右侧的饼图可以旋转,旋转到每个颜色的时候,左侧的文字会变化。好的,千里之行始于足下,我们从第一个小版块开始

        

Creating a Custom View Class

        总说自定义控件,我们先看看什么自定义控件应该具备哪几点

        A well-designed custom view is much like any other well-designedclass. It encapsulates a specific set of functionality with an easy to useinterface, it uses CPU and memory efficiently, and so forth. In addition tobeing a well-designed class, though, a custom view should:

•Conform to Android standards 

    •Provide custom styleable attributes that work with Android XML layouts

    •Send accessibility events

    •Be compatible with multiple Android platforms.

The Android framework provides a set of base classes and XML tags tohelp you create a view that meets all of these requirements. This lessondiscusses how to use the Android framework to create the core functionality ofa view class.

        一个良好的设计自定义控件是多么像任何其它设计好的类,它用一个简单好用的接口封装了一个特殊的方法集合。它能有效的使用CPU和内存等等。此外,为了设计一个良好的自定义类还需要做到以下几点:

符合Android标准

•提供自定义styleable属性的xml文件(也就是常见的attrs.xml)

拥有可访问的事件

•兼容多种Andriod平台

Android框架提供了一个基础类和xml标签的集合来帮助你创建满足你需求的控件。这一节来讨论怎么样使用Android框架来创建一个核心功能的控件(视图)类

SubClass a View

All of the view classes defined in the Android framework extendView. Your custom view can also extend View directly, or you can save time byextending one of the existing view subclasses, such as Button.

To allow theAndroid Developer Toolsto interact with your view, ata minimum you must provide a constructor that takes a Context and anAttributeSet object as parameters. This constructor allows the layout editor tocreate and edit an instance of your view.

class PieChart extends View {
    public PieChart(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
}

所有在Android框架里定义的视图类都继承于View,你的自定义控件也可以直接继承View,或者你可以为了节省时间直接集成已个已经存在的视图子类,比如说 Button

       为了使adt能够作用于你的自定义view,最少你得提供一个带有Context和AttributeSet作为参数的构造方法,这个构造方法允许你在布局编辑器里创建并且修改你的自定义控件的实例.

Define Custom Attributes

        To add a built-in View to your user interface, you specify it in anXML element and control its appearance and behavior with element attributes.Well-written custom views can also be added and styled via XML. To enable thisbehavior in your custom view, you must:

•Definecustom attributes for your view in a <declare-styleable> resource element

•Specifyvalues for the attributes in your XML layout

•Retrieveattribute values at runtime

•Apply theretrieved attribute values to your view

This section discusses how to define custom attributes and specify their values. The next section deals with retrieving and applying the values at runtime.

To define custom attributes, add<declare-styleable>resources to your project. It's customary to put these resources into ares/values/attrs.xml file. Here's an example of anattrs.xml file:

添加一个固有的控件到你的接口里,你可以在xml元素里用元素属性制定它的外观和行为。写的很好的自定义控件可以加入样式。在你的自定义控件里启动行为,你必须:

在<declare-styleable>元素里为你的控件里定义一个自定义属性

在xml布局文件里制定属性的值

在运行时取回属性值

应用取回在你的控件里的属性。

这一部分讨论怎么样定义一个自定义属性并且指定他们的值,下一节讲解在运行时回收和运行自定义属性和他们的值。(这块翻译的有些离谱)

定义自定义属性,添加<declare-styleable>到你的工程下,一般习惯性将这些资源放在res/values/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>


This code declares two custom attributes, showText andlabelPosition, that belong to a styleable entity named PieChart. The name ofthe styleable entity is, by convention, the same name as the name of the classthat defines the custom view. Although it's not strictly necessary to followthis convention, many popular code editors depend on this naming convention toprovide statement completion.

Once you define the custom attributes, you can use them in layoutXML files just like built-in attributes. The only difference is that yourcustom attributes belong to a different namespace. Instead of belonging to thehttp://schemas.android.com/apk/res/android namespace, they belong to http://schemas.android.com/apk/res/[your package name]. For example, here's howto use the attributes defined for PieChart:

In order to avoid having to repeat the long namespace URI, thesample uses an xmlns directive. This directive assigns the alias custom to thenamespace http://schemas.android.com/apk/res/com.example.customviews. You canchoose any alias you want for your namespace.

Notice the name of the XML tag that adds the custom view to thelayout. It is the fully qualified name of the custom view class. If your viewclass is an inner class, you must further qualify it with the name of theview's outer class. further. For instance, the PieChart class has an innerclass called PieView. To use the custom attributes from this class, you woulduse the tag com.example.customviews.charting.PieChart$PieView.

这些代码声明了两个自定义属性,showText和labelPosition,他们属于一个叫PieChart 的styleable entity(样式化实体),这个样式话实体的名字是根据惯例:与自定义控件类的名字保持一致。虽然这并非严格要求遵守这个惯例,许多常用的代码编辑器依靠这个命名规则来提供完成声明。

一旦你定义了自定义属性,你可以在布局文件中像使用固有属性一样使用它们。唯一不同的一点是你的自定义属性属于不同的命名空间,而不是http://schemas.android.com/apk/res/android这个命名空间。它们属于http://schemas.android.com/apk/res/[yourpackage 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 directive,这个directive分配自定义别名给命名空间。http://schemas.android.com/apk/res/com.example.customviews你可以为你的命名空间选择任何你想要的别名

注意,布局文件中自定义控件的xml标签的名字,必须是自定义控件类的全名(包名+类名),如果你的控件类是个内部类,你必须用它所在的外部类的名字进一步修饰它。举个例子,PieChart这个类有一个内部类名字叫PieView,这个类使用了自定义的属性,你应该用这个标签om.example.customviews.charting.PieChart$PieView

Apply Custom Attributes

When a view is created from an XML layout, all of the attributes inthe XML tag are read from the resource bundle and passed into the view'sconstructor as an AttributeSet. Although it's possible to read values from theAttributeSet directly, doing so has some disadvantages:

•Resourcereferences within attribute values are not resolved

•Styles arenot applied

Instead, pass the AttributeSet toobtainStyledAttributes(). This method passes back a TypedArray array of valuesthat have already been dereferenced and styled.

The Android resource compiler does a lot of work for youto make calling obtainStyledAttributes() easier. For each<declare-styleable> resource in the res directory, the generated R.javadefines both an array of attribute ids and a set of constants that define theindex for each attribute in the array. You use the predefined constants to readthe attributes from the TypedArray. Here's how the PieChart class reads itsattributes:

Note that TypedArray objects are a sharedresource and must be recycled after use

当一个来自布局文件的View被创建,xml标签里的所有属性通过view的构造方法里的AttributeSet被资源包读出。虽然它可以从直接从属性里读取值,这样做有一些缺点。

资源引用的属性值不被解析

样式不生效

反而,通过ArrtibuteSet obtainStyledArrtibutes(),这个方法通过返回一个TypedArray,一个含有已经取消引用并且样式化的数组。(context.getTheme().obtainStyledAttributes()返回TypedArray).

Android资源编译器为你做了很多工作,调用obtaionStyledAttributes()更简单,遍历<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();
   }
}


需要注意的是,TypedArray对象是一个共享的资源,所以必须在使用以后回收掉他

Properties and Events

Attributes are a powerful way of controlling thebehavior and appearance of views, but they can only be read when the view isinitialized. To provide dynamic behavior, expose a property getter and setterpair for each custom attribute. The following snippet shows how PieChartexposes a property called showText:

Notice that setShowText calls invalidate() andrequestLayout(). These calls are crucial to ensure that the view behavesreliably. You have to invalidate the view after any change to its propertiesthat might change its appearance, so that the system knows that it needs to beredrawn. Likewise, you need to request a new layout if a property changes thatmight affect the size or shape of the view. Forgetting these method calls cancause hard-to-find bugs.

Custom views should also support event listeners tocommunicate important events. For instance, PieChart exposes a custom eventcalled OnCurrentItemChanged to notify listeners that the user has rotated thepie chart to focus on a new pie slice.

It's easy to forget to expose properties and events,especially when you're the only user of the custom view. Taking some time tocarefully define your view's interface reduces future maintenance costs. A goodrule to follow is to always expose any property that affects the visibleappearance or behavior of your custom view.

Attributes是一种用来控制view外观和行为的强大方式,但是它在view初始化的时候是只读的。提供一个动态的行为,暴露每一个属性的gettersetting的方法。下面的代码片段展示PieChart如何暴露它showText的属性的

public boolean isShowText() {
   return mShowText;
}

public void setShowText(boolean showText) {
   mShowText = showText;
   invalidate();
   requestLayout();
}

需要注意的是setShowText 调用了invalidate()和requestLayout(),这是两个方法保证view表现正常的关键。你必须废止这个view,在任何通过改变它属性改变它的外观行为以后。以便系统知道它需要被重绘。同样的,如果一个属性改变影响到大小或者视图的形状你需要请求一个新的layout。忘记调用这些方法会引发很难发现的BUG。

自定义控件也应该支持事件监听从而与重要事件沟通。举个例子,PieChart暴露了一个叫做OnCurrentItemChanged自定义事件,用来通知监听器用户旋转了饼图并到了新的一块

很容易忘记暴露属性和事件,尤其当你是自定义控件的唯一使用者的时候。花点时间小心定义你的自定义控件的接口,避免以后维护的开销。一个很好的规则遵循,经常暴露任何影响你自定义控件可见外观或者行为的属性。


Ok,这节的基本内容都在这了。

总结一下吧。

1.自定义控件要遵循几个要求

符合Android规范

提供自定义的styleable 属性文件。(为了防止前文误导读者,这个xml文件不一定叫attrs.xml,叫什么都行,但里面要declare-styleable这个节点,而节点的名字通常要与类名相同) 

提供可访问事件

兼容多种Android平台

2.自定义控件直接或间接继承View,为了使adt能作用于你的view,你要提供Context和AttributeSet的构造参数。

3.在xml文件中为你的控件定义属性,并在运行时通过TypedArray取值。记得最终要回收TypedArray,因为它是共享资源

4.暴露自定义控件里每一个属性的getter和setter方法,并注意在setter方法中,记得调用invalidate()方法,如果有需要,调用requestLayout()方法

下面是Google的官方文档地址和Demo下载地址

文档:http://developer.android.com/training/custom-views/create-view.html#customattr

Demo:http://commondatastorage.googleapis.com/androiddevelopers/shareables/training/CustomView.zip

:本人英语水平也着实有限,有翻译的不对的地方欢迎指正批评!



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值