以前因为工作的关系,对于自定义控件用的少之又少,无非就是把几个控件放置到ViewGroup内部,然后提供开放方法,就成了一个所谓的自定义控件,但是这种小伎俩太简单,面试的时候这点东西根本Hold不住场,所以工作之余还是得把这块补补,也好加深一下对控件的理解。
好,啰嗦了挺多的,我们先看一下实现的效果:
只是截取了一部分运行时图,这个控件只是重写了onDraw方法,其它一一保留给View,简单说一下这个的实现方式:
使用一只画笔用来画等距的四个圆,使用另外一只画笔画一个标准的角度渐变图。
Android提供了标准的渐变图,同样在PhotoShop中我们一样可以找到这些渐变图,这样一来,我们就可以根据UI设计师设计的套路来做同样的效果了:
线性渐变图
径向渐变图
菱形渐变图
角度渐变图
对称渐变图
好了,我们将这些基本的图绘制出来之后,它还是个静态的,我们需要将它动起来,那怎么使它动起来呢,对,我们需要线程来驱动它进行重绘,需要注意的是,线程一定要出口。
开启线程有两种方式,一种是传统的开线程的方式,使用Thread。另一种则使用向主线程消息队列中发送消息来驱动。我们使用第二种:hander.postRunnable。
来贴一下整体的代码:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.view.View;/*** 雷达显示控件* Created by Sahadev on 2015/12/29.* 邮箱:sahadev@foxmail.com*/
public class RadarView extends View implements Runnable {private boolean threadFlag = true;private int rotate = 0;//用于画圆的画笔private Paint circlePaint;//用于画扫描图像private Paint shaderPaint;//获得用于画圆的坐标位置以及半径int x, y;//设置扫描图像的坐标矩阵Matrix matrix = new Matrix();//用于绘制扫描图像Shader mShader;public RadarView(Context context) {this(context, null);}public RadarView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public RadarView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);//为了避免在onDraw中重复创建对象,所以将一些初始化工作放入构造方法中来做circlePaint = new Paint();circlePaint.setColor(Color.WHITE);//设置画笔的宽度circlePaint.setStrokeWidth(1);//设置抗锯齿模式circlePaint.setAntiAlias(true);circlePaint.setFlags(Paint.ANTI_ALIAS_FLAG);//设置画笔风格circlePaint.setStyle(Paint.Style.STROKE);shaderPaint = new Paint();shaderPaint.setAntiAlias(true);shaderPaint.setFlags(Paint.ANTI_ALIAS_FLAG);//设置画笔风格为填充模式shaderPaint.setStyle(Paint.Style.FILL);postDelayed(this, 100);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//计算圆的坐标值及半径y = getMeasuredHeight() / 2;x = getMeasuredWidth() / 2;//为矩阵设置旋转坐标matrix.setRotate(rotate, x, y);//为了避免重复创建对象,则使用这种方式if (mShader == null)mShader = new SweepGradient(x, y, Color.TRANSPARENT, Color.BLUE);mShader.setLocalMatrix(matrix);shaderPaint.setShader(mShader);//画一个扫描图像canvas.drawCircle(x, y, x, shaderPaint);//画四个等距圆canvas.drawCircle(x, y, x, circlePaint);canvas.drawCircle(x, y, x / 2, circlePaint);canvas.drawCircle(x, y, x / 4 * 3, circlePaint);canvas.drawCircle(x, y, x / 4, circlePaint);}@Overridepublic void run() {if (threadFlag) {rotate++;postInvalidate();//如果到了360度,则重新开始rotate = rotate == 360 ? 0 : rotate;//一秒延迟这个任务postDelayed(this, 1);}}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();//停止循环threadFlag = false;}}
好,这样运行起来就是我们图例所显示的样子,最后看一下内存使用以及CPU使用情况:
都不是很多,这只是最基本的,我们还可以将它进一步的优化。
有什么疑问欢迎留言讨论。