Android UI
- 前几天看见一篇文章Android 雷达扫描动画效果实现 就学习了一下,在此做了笔记并做了一些修改
- 自定义ScanRadar继承了View控件
- 初始化几个画笔
private void initPaint() {
//用来绘画直线的画笔
mPaintLine = new Paint(Paint.ANTI_ALIAS_FLAG); // 消除锯齿
mPaintLine.setAntiAlias(true);
mPaintLine.setColor(Color.WHITE);
mPaintLine.setStyle(Style.STROKE);
mPaintLine.setStrokeWidth(5);
//用来绘画背景圆的画笔
mPaintCircle = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintCircle.setAntiAlias(true);
mPaintCircle.setColor(0x99000000);
//实心圆style
mPaintCircle.setStyle(Style.FILL);
//绘画圆渐变色的画笔
mPaintSector = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintSector.setAntiAlias(true);
mPaintSector.setStyle(Style.FILL);
//绘画实点
mPaintPoint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintPoint.setAntiAlias(true);
mPaintPoint.setColor(Color.WHITE);
mPaintPoint.setStyle(Style.FILL);
}
- 重写了onMeasure函数,在此函数中获得控件的宽度作为雷达的直径
//重写onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获得控件的宽度 宽度<=高度 viewSize = getMeasuredWidth(); //初始化一个颜色渲染器 SweepGradient shader = new SweepGradient(viewSize /2.0f, viewSize/2.0f, Color.TRANSPARENT, Color.GREEN); //mPaintSector设置颜色渐变渲染器 mPaintSector.setShader(shader); }
- 然后重写onDraw函数
@Override
protected void onDraw(Canvas canvas) {//绘制背景圆 canvas.drawCircle(viewSize/2.0f,viewSize/2.0f, viewSize/2.0f, mPaintCircle); //绘制空心圆 canvas.drawCircle(viewSize/2.0f,viewSize/2.0f, viewSize/4.0f, mPaintLine); canvas.drawCircle(viewSize/2.0f,viewSize/2.0f, viewSize/8.0f, mPaintLine); canvas.drawCircle(viewSize/2.0f,viewSize/2.0f, viewSize/2.0f, mPaintLine); //绘制水平垂直两个直径 canvas.drawLine(0, viewSize/2.0f,viewSize, viewSize/2.0f, mPaintLine); canvas.drawLine(viewSize/2.0f,0,viewSize/2.0f,viewSize, mPaintLine); //可以用来显示实心点,使用了接口让外面调用 if(mListener != null){ mListener.OnUpdate(canvas,mPaintPoint,viewSize/2.0f,viewSize/2.0f,viewSize/2.0f); } //把画布的多有对象与matrix联系起来 if(matrix != null){ canvas.concat(matrix); } //绘制颜色渐变圆 canvas.drawCircle(viewSize/2.0f,viewSize/2.0f, viewSize/2.0f, mPaintSector); super.onDraw(canvas); }
- 然后剩下动画更新了
在这使用了一个线程来更新界面展示
class ScanThread extends Thread{
private View view;public ScanThread(View view) { super(); this.view = view; } @Override public void run() { while(isRunning){ if(isStart){ start += 1; view.post(new Runnable() { @Override public void run() { //创建一个矩阵 matrix = new Matrix(); //矩阵设置旋转 matrix.preRotate(start*direction, viewSize/2.0f, viewSize/2.0f); //重画 view.invalidate(); } }); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
// 需要注意的是,线程的结束和开启是使用了两个boolean的变量来控制
isRunning ,isStart分别表示了线程的状态
view.post(Runable);函数的作用是
Causes the Runnable to be added to the message queue. The runnable will be run on the user interface thread.
意思就是任务会放到ui线程里执行
- 最后 暴露出一个接口让外界更新发现的目标显示
public OnPointUpdateListener mListener;//更新point的接口
public interface OnPointUpdateListener{//onDraw中的画布,paintPoint绘制实心点的画笔,cx,cy,radius 雷达的圆心坐标,半径
public void OnUpdate(Canvas canvas,Paint paintPoint,float cx,float cy,float radius);
}public void setOnPointUpdateListener(OnPointUpdateListener listener){
this.mListener = listener;
}
- 然后贴出完整的代码
package com.linge.scanradar.view.radar;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.SweepGradient;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.view.View;
public class ScanRadar extends View {
//控件宽度<=高度 //雷达直径 private float viewSize; //画笔 private Paint mPaintSector; private Paint mPaintCircle; private Paint mPaintLine; private Paint mPaintPoint; //线程是否在执行 private boolean isRunning = false; //任务是否已经开始 private boolean isStart = false; //记录并设置旋转角度 private int start = 0; //雷达旋转方向 //顺时针 public final static int CLOCK_WISE = 1; //逆时针 public final static int ANSI_CLOCK_WISE = -1; public final static int DEFAULT_DIRECTION = CLOCK_WISE; private int direction = DEFAULT_DIRECTION; private ScanThread scanThread; //线程 private Matrix matrix; //矩阵 public ScanRadar(Context context, AttributeSet attrs) { this(context, attrs,0); } public ScanRadar(Context context) { this(context,null); } public ScanRadar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initPaint(); } private void initPaint() { //用来绘画直线的画笔 mPaintLine = new Paint(Paint.ANTI_ALIAS_FLAG); // 消除锯齿 mPaintLine.setAntiAlias(true); mPaintLine.setColor(Color.WHITE); mPaintLine.setStyle(Style.STROKE); mPaintLine.setStrokeWidth(5); //用来绘画背景圆的画笔 mPaintCircle = new Paint(Paint.ANTI_ALIAS_FLAG); mPaintCircle.setAntiAlias(true); mPaintCircle.setColor(0x99000000); //实心圆style mPaintCircle.setStyle(Style.FILL); //绘画圆渐变色的画笔 mPaintSector = new Paint(Paint.ANTI_ALIAS_FLAG); mPaintSector.setAntiAlias(true); mPaintSector.setStyle(Style.FILL); //绘画实点 mPaintPoint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaintPoint.setAntiAlias(true); mPaintPoint.setColor(Color.WHITE); mPaintPoint.setStyle(Style.FILL); } @Override protected void onDraw(Canvas canvas) { //绘制背景圆 canvas.drawCircle(viewSize/2.0f,viewSize/2.0f, viewSize/2.0f, mPaintCircle); //绘制空心圆 canvas.drawCircle(viewSize/2.0f,viewSize/2.0f, viewSize/4.0f, mPaintLine); canvas.drawCircle(viewSize/2.0f,viewSize/2.0f, viewSize/8.0f, mPaintLine); canvas.drawCircle(viewSize/2.0f,viewSize/2.0f, viewSize/2.0f, mPaintLine); //绘制水平垂直两个直径 canvas.drawLine(0, viewSize/2.0f,viewSize, viewSize/2.0f, mPaintLine); canvas.drawLine(viewSize/2.0f,0,viewSize/2.0f,viewSize, mPaintLine); //显示实心点 if(mListener != null){ mListener.OnUpdate(canvas,mPaintPoint,viewSize/2.0f,viewSize/2.0f,viewSize/2.0f); } //把画布的多有对象与matrix联系起来 if(matrix != null){ canvas.concat(matrix); } //绘制颜色渐变圆 canvas.drawCircle(viewSize/2.0f,viewSize/2.0f, viewSize/2.0f, mPaintSector); super.onDraw(canvas); } //重写onMeasure @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //获得控件的宽度 宽度<=高度 viewSize = getMeasuredWidth(); //初始化一个颜色渲染器 SweepGradient shader = new SweepGradient(viewSize /2.0f, viewSize/2.0f, Color.TRANSPARENT, Color.GREEN); //mPaintSector设置颜色渐变渲染器 mPaintSector.setShader(shader); } //设置循环的方向 public void setDirection(int d){ if(d != CLOCK_WISE && d != ANSI_CLOCK_WISE){ throw new IllegalStateException("only contonst CLOCK_WISE ANSI_CLOCK_WISE"); } this.direction = d; } //线程开启 public void start(){ scanThread = new ScanThread(this); scanThread.setName("radar"); isRunning = true; isStart = true; scanThread.start(); } //线程结束 public void stop(){ if(isStart){ isStart = false; isRunning = false; } } class ScanThread extends Thread{ private View view; public ScanThread(View view) { super(); this.view = view; } @Override public void run() { while(isRunning){ if(isStart){ start += 1; view.post(new Runnable() { @Override public void run() { //创建一个矩阵 matrix = new Matrix(); //矩阵设置旋转 matrix.preRotate(start*direction, viewSize/2.0f, viewSize/2.0f); //重画 view.invalidate(); } }); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } } } } public OnPointUpdateListener mListener; //更新point的接口 public interface OnPointUpdateListener{ public void OnUpdate(Canvas canvas,Paint paintPoint,float cx,float cy,float radius); } public void setOnPointUpdateListener(OnPointUpdateListener listener){ this.mListener = listener; }
}
//MainActivity中的代码
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ScanRadar radar = (ScanRadar) findViewById(R.id.scan_radar); //随机获得一组坐标 final int[] point_x = getRandomArray(50, 10); final int[] point_y = getRandomArray(60, 10); radar.setOnPointUpdateListener(new OnPointUpdateListener() { @Override public void OnUpdate(Canvas canvas, Paint paintPoint, float cx, float cy, float radius) { for (int i = 0; i < point_y.length; i++) { canvas.drawCircle(cx+point_x[i], cy+point_y[i], 5, paintPoint); } } }); radar.start(); }
//最后感谢Android 雷达扫描动画效果实现 的作者让我学会了一招