自定义组件使用总结

一 概述

自定义组件有三种方式:
1. 继承现有组件,拓展其功能;
2. 组合现有控件,实现模板化;
3. 继承View,重写onDraw()方法,进行重绘;继承ViewGroup,重写相关方法,实现布局。

二 继承现有组件,拓展其功能

项目位置:E:\code\CustomViews\StudyTextView 以及 E:\code\CustomViews\StudyTextView0
运行效果:StudyTextView 通过点击自定义组件RotateTextView,实现其文字内容的旋转
StudyTextView0 实现自定义组件PaintDoubleWordsTextView,使其中的文字可随手指移动
总结:
以上两个自定义组件的实现原理均为继承现有组件,并在现有组件的基础上进行功能拓展。
具体而言,步骤如下:
1. 根据需要实现的功能选择所要拓展的组件;
2. 自定义类继承所要拓展的组件,这时,有以下几个重要的方法需要重写或使用:
重写:
构造方法 – 如果要在XML文件中直接使用自定义组件,需要实现具有两个参数的构造方法。这两个参数分别为:Context 与 AttributeSet。
在构造方法中,主要实现获取XML布局文件中写入的自定义属性的值。
在这之前,需要在res/values目录下定义attrs.xml文件,该文件中定义了自定义组件支持的自定义属性。具体写法为:在resources标签下加入declare-styleable标签,并给其一个标签名,一般选择自定义类名作为标签名;然后,在后面自定义属性,以标签attr包裹,内容包括自定义属性名name与自定义属性的类型format。形式如下所示:

<resources>
    <declare-styleable name="一般为自定义控件类名">
        <attr name="自定义属性名" format="">
        <attr name="自定义属性名" format="">
        ... ...
    </declare-styleable>
</resources>

这就定义了自定义控件的自定义属性,之后就可以在XML布局文件中使用这些自定义属性了。
使用的时候,要在xml文件中写入xmlns,即xml命名空间,形式为:
xmlns:custom=”http://schemas.android.com/apk/res-auto”
或:
xmlns:custom=”http://schemas.android.com/apk/manifest中的package名”
之后就可以“custom:自定义属性名”来使用自定义属性了。
在构造方法中,通过操作TypedArray来获取自定义属性内容,代码如下:

TypedArray options = context.obtainStyledAttributes(attrs, R.styleable.PaintDoubleWordsTextView, 0, 0);

int numOfOptions = options.getIndexCount();

for (int i = 0; i < numOfOptions; ++i) {
    int index = options.getIndex(i);
    switch (i) {
        case R.styleable.PaintDoubleWordsTextView_word0 :
            word0 = options.getString(index);
            break;
        case R.styleable.PaintDoubleWordsTextView_word0Size :
            word0Size = options.getInt(index, DEFAULT_TEXT_SIZE);
            break;
        case R.styleable.PaintDoubleWordsTextView_word1 :
            word1 = options.getString(index);
            break;
        case R.styleable.PaintDoubleWordsTextView_word1Size :
            word1Size = options.getInt(index, DEFAULT_TEXT_SIZE);
            break;
    }
}
options.recycle(); // 勿忘

onDraw() – 在该方法中绘制需要显示的内容
onDraw()方法的参数是一个Canvas对象,通过该对象即可实现自定义组件的内容绘制。
使用:
invalidate() – 导致onDraw()被调用,刷新View
requestLayout() – 当前View的布局失效时就需要进行调用
以上两个方法更新过View后就要进行调用。
format包括:

类型 含义
boolean 布尔值
integer 整数值
float 浮点值
fraction 百分数
string 字符串
color 颜色
dimension 尺寸值
enum 枚举
flag 位或运算
reference 资源引用

使用情况:
enum:

<attr name="属性名">
    <enum name="名称" value="值" />
    <enum name="名称" value="值" />
</attr>

flag:

<attr name="属性名">
    <flag name="名称" value="值" />
    <flag name="名称" value="值" />
    ... ...
</attr>

format参考:http://blog.csdn.net/mayingcai1987/article/details/6216655
//////
以上内容可参考:http://blog.csdn.net/allen315410/article/details/39343401
//////

三 控件组合,模板化使用

这种自定义组件的方式并不创建新的组件,只是将已有组件进行组合,然后将组合好的整体当做模板来使用。
关键方法:
1
LayoutInflater.from(Context).inflate(自定义布局id, 为加载好的布局指定父布局);
通过以上关键代码来将自定义布局载入程序中,之后就可以通过findViewById来获取自定义布局文件中的组件了。
步骤如下:
1. 根据需求进行组件组合
根据需求对组件进行组合,构成模板。此处主要是实现一个XML布局文件。
2. 拓展Layout类
实现Layout类的子类,在其具有两个参数的构造方法中加载上一步实现的布局文件,然后获取其中定义的组件。
3. 控制组件显示,实现事件响应
根据需求,操作上一步获取的组件,控制组件显示并实现具体的事件响应。

示例程序:E:\code\ExampleBest\test_selfdefinewidget

四 继承View,重写onDraw()方法,进行重绘;继承ViewGroup,重写相关方法,实现布局

继承View与继承ViewGroup是不同的。继承View是要自定义组件,而继承ViewGroup则要自定义布局
对于自定义组件而言,所需要考虑比较少,只有两点需要注意:
1. 对于Touch事件的处理;
根据事件处理机制,当重写了onTouch方法后,要注意返回true之后会导致onTouchEvent方法不再执行,进而影响到click的执行与滑动的效果;同时,还要注意对ACTION的DOWN处理后返回false时,会导致后续的UP、MOVE事件无法获取,也就是会阻止事件的层级传递。
2. 重写onDraw方法绘制需要的UI。
根据Android绘制原理,对于自定义View的绘制,只需要重写onDraw方法即可,在其中使用所提供的Canvas画布绘制所需要的效果。
至于测量与布局,自定义组件不需要考虑太多,这是由布局组件来考虑的问题。事实上,View类中的measure方法是一个final方法,子类无法重写,对于子类,只需要重写真正进行测量的onMeasure方法即可,在View类中的onMeasure实现里,只是去调用了setMeasuredDimension这个final方法去保存了组件的宽、高信息。从直接继承自View的TextView源码情况来看,其重写了onMeasure方法去根据padding等信息来计算宽、高数据。View类中的layout方法则直接在注释中说明,子类不需要重写该方法,具有子组件的子类,需要重写onLayout方法,在其中调用子组件的layout方法,而View类中的onLayout方法则是一个空方法。从TextView源码来看,其并没有重写layout方法(因为不需要),所重写的onLayout方法也没有进行重要的操作。所以,在自定义View组件时,为了实现较精细的测量,一般需要重写onMeasure方法,对于onLayout方法,一般不用实现
对于自定义布局,就要考虑对于测量与布局的处理问题
1. 测量
这一步根据Android绘制原理,需要重写onMeasure方法,在其中获取各个子组件的dimension,计算出布局的dimension,然后调用setMeasuredDimension方法设置好布局的尺寸。ViewGroup中没有重写onMeasure方法,子类必须自己进行实现。
2. 布局
这一步需要重写onLayout方法,在其中调用各个子组件的layout方法,让子组件完成对自己的布局。在ViewGroup中,onLayout方法是一个抽象方法,自定义的布局必须实现该方法,在其中对子组件的layout方法进行调用。而ViewGroup中的layout方法是一个final方法,不能被重写,其中则简单的调用了Super的layout方法(super.layout())。关于View的layout方法,一般在进行继承View自定义组件时不需要重写,至于View类中的onLayout方法,是一个空方法,不进行任何操作。
至于绘制,则由ViewRootImpl类中的performTraversals方法调用performDraw进而调用组件的onDraw方法实现,不需要在ViewGroup中考虑。

你可能感兴趣的:(android,view,自定义控件,ViewGroup)