以前因为工作的关系,对于自定义控件用的少之又少,无非就是把几个控件放置到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. * 邮箱:[email protected] */ 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); } @Override protected 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); } @Override public void run() { if (threadFlag) { rotate++; postInvalidate(); //如果到了360度,则重新开始 rotate = rotate == 360 ? 0 : rotate; //一秒延迟这个任务 postDelayed(this, 1); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); //停止循环 threadFlag = false; } }
都不是很多,这只是最基本的,我们还可以将它进一步的优化。
有什么疑问欢迎留言讨论。