Android必知必会之自定义组件

一 概述

自定义组件是Android工程师必须了解并且经常会使用的知识点,本文就是对该知识点的简单总结。

具体而言,自定义组件有三种方式:

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

  2. 组合现有组件,实现模板化

  3. 直接继承View,重写onDraw方法,进行重绘

    直接继承ViewGroup(或其子类),重写onLayout/onMeasure方法,进行自定义布局

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

步骤如下:

  1. 根据要实现的效果,找到功能相近的组件

  2. 继承该组件,实现自有功能

    具体而言,以下几个方法需要注意,

    构造方法

    首先,如果要在XML文件中直接使用自定义组件,需要实现具有两个参数的构造方法。

    在构造方法中,主要实现获取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。

    requestLayout()

    触发整个绘制流程,onMeasure/onLayout/onDraw。

三 组合组件,模板化使用

这种自定义组件的方式并不创建新的组件,只是将已有组件进行组合,然后将组合好的整体当做模板来使用。

关键方法:

LayoutInflater.from(Context).inflate(自定义布局id, 为加载好的布局指定父布局);

通过以上关键代码来将自定义布局载入程序中,之后就可以通过findViewById来获取自定义布局文件中的组件了。

步骤如下:

  1. 根据需求组合组件

    根据需求对组件进行组合,构成模板。

    此处主要是实现一个XML布局文件。

  2. 拓展Layout类

    实现Layout类的子类,在其具有两个参数的构造方法中加载上一步实现的布局文件,然后获取其中定义的组件。

  3. 控制组件显示,实现事件响应

    根据需求,操作上一步获取的组件,控制组件显示并实现具体的事件响应。

四 继承View & ViewGroup

继承View与继承ViewGroup是不同的。继承View是要自定义组件,而继承ViewGroup则要自定义布局

对于自定义组件而言,所需要考虑比较少,只有两点需要注意:

  1. 对于Touch事件的处理

    根据事件处理机制,当重写了onTouch方法后,要注意返回true之后会导致onTouchEvent方法不再执行,进而影响到click的执行与滑动的效果;同时,还要注意对ACTION的DOWN处理后返回false时,会导致后续的UP、MOVE事件无法获取,也就是会阻止事件的层级传递。

  2. 重写onDraw方法绘制需要的UI

    根据Android绘制原理,自定义组件只需要重写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方法,不能被重写,其中则简单的调用了View的layout方法(super.layout())。

    关于View的layout方法,一般在进行继承View自定义组件时不需要重写,至于View类中的onLayout方法,是一个空方法,不进行任何操作。

    至于绘制,则由ViewRootImpl类中的performTraversals方法调用performDraw进而调用组件的onDraw方法实现,不需要在ViewGroup中考虑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值