波形自定义控件(四):原理解析之控件绘制与动画效果

上一篇讲解了WaveLoadingView的测量步骤,本篇将讲解它的绘制原理。

前期准备

在讲解Android自定义控件绘制之前,我们首先需要知道动画效果是怎么实现的。

下面这张图小球从左沿着一条弧线的轨迹运动到右边。
在这里插入图片描述
把它的动作分解一下。
在这里插入图片描述
我们称每一张图片为帧,小球运动的动画效果其实就是帧1~帧5快速切换产生的。动作分解的越细,动画的效果也越好,当然帧数变多了,动画的体积也就跟着变大了。

onDraw方法

上一篇我们完成了控件的测量工作,但此时如果我们把控件拿来使用估计什么都不会显示。那是因为onMeasue只是规定了控件的宽高,还没有把它的样子画出来。绘制工作是在onDraw方法里完成的。我们来看看onDraw方法。

@Override
protected void onDraw(Canvas canvas) {
	super.onDraw(canvas);    
}

说到onDraw方法,我们还得先认识两个类:Canvas 和 Paint
Canvas叫画布,是用来显示图像的,只有在画布和控件的交集处绘制图像才能显示出来,说的有点抽象,来看看下图。在这里插入图片描述
假如蓝色方框部分为控件的显示范围,红色方框为画布的范围,那么只有在黄色区域绘制的图像才会显示。

再看看坐标系,Android规定屏幕的从左往右为x轴正方向,从上到下为y轴正方向。默认下,画布和控件的范围是一致的,如下图。
在这里插入图片描述
再来讲讲Paint类,画笔类,从名字上也可以知道它是用来绘制图像的,一些基本形状(圆、矩形、弧线等)都是通过它绘制到Canvas上的。当然,还可以设置颜色和透明度。例如在Canvas上绘制一个100*50像素的矩形。

canvas.drawRect(0,0,100,50,mPaint);

Canvas类和Paint类还有很多强大的功能,可以看看这篇文章

回过来看看onDraw方法。假设我们要在控件正中央画一个50*50像素的红色矩形,代码如下。

@Override
protected void onDraw(Canvas canvas) {
	super.onDraw(canvas);
	int width = getWidth();
	int height = getHeight();  
	Paint paint = new Paint();
	paint.setColor(Color.RED);
    int left = (width - 50) / 2;
    int top = (height - 50) / 2;
    int right = left + 50;
    int bottom = top + 50;
	canvas.drawRect(left,top,right,bottom,paint);
}

这样可以在控件正中央绘制一个50*50像素的红色正方形,但其实代码还有很多瑕疵,后面会讲解。

调用一次onDraw后,一帧画面就绘制出来了。这么说来要想实现动画效果就需要多次调用onDraw方法了。

回到刚刚小球的分解动作。
在这里插入图片描述
每次调用onDraw方法,在指定位置画出圆,连贯起来就可以实现动画效果了。这里需要再认识invalidate方法。invalidate方法可以强制让应用重新绘制这个控件。调用几次invalidate方法就相当于调用了多次onDraw。

现在希望小球从左到右移动,动画效果时间为1000ms,那么每250ms调用一次invalidate方法就行了。可要怎么精确把握调用时间呢,这里就需要用到Handler了。

通过Handler实现动画效果

通过以下方式可以实现每250ms调用一次onDraw的效果。

public void startAnim(){
	mCount = 0;
	invalidate();
	sendEmptyMessageDelayed(0,250);
}

private Hnadler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
        	mCount++;
        	if(mCount <= 4){
        		invalidate();
            	sendEmptyMessageDelayed(0,250);
            }
        }    
}

但这么做会有内存泄漏的风险,本篇讲解的重点是实现动画效果,性能优化会放在下一篇来讲。

再来简单看看WaveLoadingView的onDraw方法。

@Override
    protected void onDraw(Canvas canvas) {
    	//1
        prepareElementsX();
        //2
        prepareElementsY();

        switch(mType){
            case IMAGE_TYPE_TEXT:
                onDrawText(canvas);
                break;

            case IMAGE_TYPE_CIRCLE:
                onDrawCirclesWave(canvas);
                break;

            case IMAGE_TYPE_SQUARE:
                onDrawSquareWave(canvas);
                break;

            case IMAGE_TYPE_RECT:
                onDrawRectWave(canvas);
                break;

            case IMAGE_TYPE_NOISE:
                onDrawNoiseWave(canvas);
                break;

            case IMAGE_TYPE_CUSTOM:
                onDrawDrawable(canvas);
                break;
        }
    }

代码1和代码2先计算好各元素的摆放位置,接着根据不同的风格绘制出一帧图像。在handler上定时调用invalidate方法就实现动画效果了。

最后

限于文章篇幅,文章只贴了关键代码,想深入了解此控件绘制的朋友可以到Github项目上阅读完整代码。

下一篇将讲解自定义控件的性能优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值