Android自定义View实现雷达扫描动画

最近在项目中有用到雷达扫描动画,这个效果也常被用于扫描或定位等事件,通过一个小Demo对此进行一下总结。

动画截图如下:

Android自定义View实现雷达扫描动画_第1张图片

Android的动画分两类:一类是Tween动画,就是对场景里的对象不断的进行图像变化来产生动画效果(旋转、平移、放缩和渐变)。另一类就是 Frame动画,即顺序的播放事先做好的图像,与gif图片原理类似。

此处的基本思想是通过自定义雷达View,不断刷新和绘制雷达View实现扫描Tween动画。

1. 首先自定义雷达View,RadarView.java代码如下:

package com.example.radar;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.view.View;

public class RadarView extends View{

	private Paint circlePaint;//圆形画笔
	private Paint linePaint;//线形画笔
	private Paint sweepPaint;//扫描画笔
	SweepGradient sweepGradient;//扇形渐变Shader
	int degree = 0;
	
	public RadarView(Context context){
		super(context);
	}
	
	public RadarView(Context context, AttributeSet att){
		super(context,att);
		initPaint();
	}
	
		/**
		* @param 
		* @return void
		* @Description //初始化定义的画笔  
		*/
	private void initPaint(){
		Resources r = this.getResources();
			
		circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);//圆形画笔,设置Paint为抗锯齿
		circlePaint.setARGB(255, 50, 57, 74);//设置透明度和RGB颜色
		circlePaint.setStrokeWidth(3);//轮廓宽度
		circlePaint.setStyle(Paint.Style.STROKE);
			
		linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);//线性画笔
		linePaint.setStrokeCap(Paint.Cap.ROUND);
		linePaint.setARGB(150, 50, 57, 74);
		linePaint.setStrokeWidth(2);
			
		sweepPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//雷达Shader画笔
		sweepPaint.setStrokeCap(Paint.Cap.ROUND);
		sweepPaint.setStrokeWidth(4);
		sweepGradient = new SweepGradient(0,0,r.getColor(R.color.start_color),r.getColor(R.color.end_color));
		sweepPaint.setShader(sweepGradient);
			
	}
	
	@Override
	protected void onMeasure(int wMeasureSpec, int hMeasureSpec){
		int width = MeasureSpec.getSize(wMeasureSpec);
		int height = MeasureSpec.getSize(hMeasureSpec);
		int d = (width>=height)?height:width; //获取最短的边作为直径
		setMeasuredDimension(d,d); //重写测量方法,保证获得的画布是正方形
	}
	
	
	@Override
	protected void onDraw(Canvas canvas){
		
		int Width = getMeasuredWidth();计算控件的中心位置
		int Height = getMeasuredHeight();
		int pointX =  Width/2;//获得圆心坐标
		int pointY = Height/2;
		
		int radius = (pointX>=pointY) ? pointY : pointX;//设置半径
		radius -= 10;//设置半径
		canvas.save();//保存Canvas坐标原点
		
		degree += 5;//扫描旋转增量度数
		canvas.translate(pointX, pointY);//设置旋转的原点
		canvas.rotate(270 + degree);
		canvas.drawCircle(0, 0, radius, sweepPaint);//绘制扫描区域
			
		canvas.restore();//恢复原Canvas坐标(0,0)		
		
		canvas.drawCircle(pointX, pointY, radius, circlePaint);//绘制3个嵌套同心圆形,使用circlePaint画笔
		circlePaint.setAlpha(100);//降低内部圆形的透明度
		circlePaint.setStrokeWidth(2);//轮廓宽度
		canvas.drawCircle(pointX, pointY, radius*2/3, circlePaint);
		canvas.drawCircle(pointX, pointY, radius/3, circlePaint);
		
		canvas.drawLine(pointX, 10, pointX, 2*radius + 10, linePaint);//绘制十字分割线 , 竖线
		canvas.drawLine(10, pointY, 2*radius + 10, pointY, linePaint);
		
		canvas.save();//保存Canvas坐标原点
		canvas.translate(10, radius+10);//设置相对横线起始坐标
		
		float s = radius/12f;//刻度间距
		float minlength = s/2;//短刻度线长度
		float maxlength = s;//长刻度线长度
		
		for(int i=0;i<24;i++){
			float fromX,toX;
			fromX=toX=s*i;
			if(i%4!=0){//与圆形重叠处不画刻度
				if(i%2!=0){
					canvas.drawLine(fromX,-minlength,toX,minlength, linePaint);//绘制X轴短刻度
				}else{
					canvas.drawLine(fromX,-maxlength,toX,maxlength, linePaint);//绘制X轴长刻度
				}
			}
		}
		
		canvas.restore();
		canvas.translate(pointX, 10);//设置相对竖线起始坐标
		for(int i=0;i<24;i++){
			float fromY,toY;
			fromY=toY=s*i;
			if(i%4!=0){
				if(i%2!=0){
					canvas.drawLine(-minlength,fromY,minlength,toY, linePaint);//绘制Y短轴刻度
				}else{
					canvas.drawLine(-maxlength,fromY,maxlength,toY, linePaint);//绘制Y长轴刻度
				}
			}
		}
		
	}
	
}

其中,需要用到Android渲染图像和图形的Shader类的渐变扫描子类SweepGradient,通过设置起始颜色color0和结束的color1,渲染出围绕中心点的闭合圆形渐变效果。

SweepGradient对象颜色定义如下:



    #696969
    #20ffffff

2. 实现RadarView的扫描动画

activity_main.xml简单布局文件,添加自定义RadarView和控制按钮,代码如下:



    
    
    

创建RadarView的扫描刷新线程RadarSweep,当在线程radarView调用postInvalidate()方法时,会刷新RadarView的onDraw()方法,随之degree变量增加旋转角度,sweepGradient产生旋转效果。

MainActivity.java代码如下:

package com.example.radar;

import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;

public class MainActivity extends Activity {

	private RadarView radarView;
	private Button btn;
	private Thread radarSweepThread;

	private boolean startRadar = true;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		radarView = (RadarView) findViewById(R.id.radar);
		btn = (Button) findViewById(R.id.btn);
		btn.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				if (startRadar) {
					btn.setText("end");
					radarView.setVisibility(View.VISIBLE);// 设置可见
					Animation radarAnimEnter = AnimationUtils.loadAnimation(
							MainActivity.this, R.anim.radar_anim_enter);// 初始化radarView进入动画
					radarView.startAnimation(radarAnimEnter);// 开始进入动画
					radarSweepThread = new Thread(new RadarSweep());// 雷达扫描线程
					radarSweepThread.start();
					startRadar = false;
				} else {
					btn.setText("start");
					Animation radarAnimEnter = AnimationUtils.loadAnimation(
							MainActivity.this, R.anim.radar_anim_exit);// 初始化radarView退出动画
					radarView.startAnimation(radarAnimEnter);// 开始进入动画
					radarView.setVisibility(View.INVISIBLE);// 设置不可见
					radarSweepThread.interrupt();// 停止扫描更新
					startRadar = true;
				}
			}
		});
	}

	/**
	 * @ClassName RadarSweep
	 * @Description 雷达扫描动画刷新线程类
	 */
	private class RadarSweep implements Runnable {
		int i = 1;

		@Override
		public void run() {
			// TODO Auto-generated method stub

			while (!Thread.currentThread().isInterrupted() && i == 1) {
				try {
					radarView.postInvalidate();// 刷新radarView, 执行onDraw();
					Thread.sleep(10);// 暂停当前线程,更新UI线程
				} catch (InterruptedException e) {
					i = 0;// 结束当前扫描线程标志符
					break;
				}
			}
		}

	}

}

其中,RadarView载入和淡出的动画代码如下:

\Radar\res\anim\radar_anim_enter.xml



    
   
   
    

\Radar\res\anim\radar_anim_exit.xml



    
    

简单的自定义雷达动画Demo介绍到此结束,如果想要更复杂和逼真的效果,还可以添加更多的效果和功能:如在雷达扫描的表盘上添加随机发光点,以及增加雷达刷新线程的控制参数,实现其他线程事件对雷达扫描的监听,如完成某个事件后自动退出雷达扫描动画等。时间有限,不在此一一赘述。



你可能感兴趣的:(Android)