Android UI 之自定义RadarView——高仿微信雷达扫描

最近看了一个视频讲了一种微信雷达扫描的实现方案,借鉴了一下,自己也写一个玩玩,与大家分享一下。基本想出来三种解决方案,根据不同需求情况选择即可。


方案一实现思路(通用):

1.自定义view 

2.重写onDraw()方法

3.画四个无锯齿空心圆

4.画以最大圆为半径的实心渐变圆

5.创建矩阵,旋转画布,重绘,并用Handler实现循环

  1. package com.ml512.radarview;  
  2.   
  3. import com.ml512.radardemo.R;  
  4.   
  5. import android.annotation.SuppressLint;  
  6. import android.content.Context;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.Color;  
  9. import android.graphics.Matrix;  
  10. import android.graphics.Paint;  
  11. import android.graphics.Paint.Style;  
  12. import android.graphics.Shader;  
  13. import android.graphics.SweepGradient;  
  14. import android.os.Handler;  
  15. import android.util.AttributeSet;  
  16. import android.view.View;  
  17. /** 
  18.  * 2015/12/06 22:49 
  19.  * @author ITjianghuxiaoxiong 
  20.  * http://blog.csdn.net/itjianghuxiaoxiong 
  21.  */  
  22. @SuppressLint("DrawAllocation")  
  23. public class RadarView extends View {  
  24.     private int w, h;// 获取控件宽高  
  25.     private Paint mPaintLine;// 画雷达圆线  
  26.     private Paint mPaintSolid;// 画雷达渐变实心圆  
  27.     private Matrix matrix;  
  28.     private int degrees;  
  29.     private Handler mHandler = new Handler();  
  30.     private Runnable mRunnable = new Runnable() {  
  31.         @Override  
  32.         public void run() {  
  33.             degrees++;  
  34.             matrix.postRotate(degrees, w / 2, h / 2);//旋转矩阵  
  35.             RadarView.this.invalidate();// 重绘  
  36.             mHandler.postDelayed(mRunnable, 55);  
  37.         }  
  38.     };  
  39.   
  40.     public RadarView(Context context) {  
  41.         this(context, null);  
  42.     }  
  43.   
  44.     public RadarView(Context context, AttributeSet attrs) {  
  45.         this(context, attrs, 0);  
  46.     }  
  47.   
  48.     public RadarView(Context context, AttributeSet attrs, int defStyleAttr) {  
  49.         super(context, attrs, defStyleAttr);  
  50.         setBackgroundResource(R.drawable.radar_bg);//雷达的背景图片(紫色满天星,可以在微信APP中直接找到图片资源)  
  51.         initPaint();  
  52.         mHandler.postDelayed(mRunnable,500);  
  53.     }  
  54.   
  55.     @Override  
  56.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  57.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  58.         w = getMeasuredWidth();//获取view的宽度  
  59.         h = getMeasuredHeight();//获取view的高度  
  60.     }  
  61.   
  62.     /** 
  63.      * 初始化画笔 
  64.      */  
  65.     private void initPaint() {  
  66.         mPaintLine = new Paint();  
  67.         mPaintLine.setColor(Color.parseColor("#CCA1A1A1"));// 设置画笔  
  68.         mPaintLine.setStrokeWidth(1);// 设置画笔宽度  
  69.         mPaintLine.setAntiAlias(true);// 消除锯齿  
  70.         mPaintLine.setStyle(Style.STROKE);// 设置空心  
  71.   
  72.         mPaintSolid = new Paint();  
  73.         mPaintSolid.setAntiAlias(true);// 消除锯齿  
  74.         mPaintSolid.setStyle(Style.FILL);//实心圆  
  75.         matrix = new Matrix();//创建组件  
  76.     }  
  77.   
  78.     @Override  
  79.     protected void onDraw(Canvas canvas) {  
  80.         //四个空心圆  
  81.         canvas.drawCircle(w / 2, h / 2, w / 6, mPaintLine);  
  82.         canvas.drawCircle(w / 2, h / 25 * w / 14, mPaintLine);  
  83.         canvas.drawCircle(w / 2, h / 212 * w / 20, mPaintLine);  
  84.         canvas.drawCircle(w / 2, h / 29 * w / 11, mPaintLine);  
  85.           
  86.         //渐变  
  87.         Shader mShader = new SweepGradient(w / 2, h / 2, Color.TRANSPARENT, Color.parseColor("#33FFFFFF"));  
  88.         mPaintSolid.setShader(mShader);  
  89.         canvas.setMatrix(matrix);  
  90.         canvas.drawCircle(w / 2, h / 29 * w / 11, mPaintSolid);  
  91.         matrix.reset();//重置矩阵,避免累加,越转越快  
  92.         super.onDraw(canvas);  
  93.     }  
  94.   
  95. }  
package com.ml512.radarview;

import com.ml512.radardemo.R;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
/**
 * 2015/12/06 22:49
 * @author ITjianghuxiaoxiong
 * http://blog.csdn.net/itjianghuxiaoxiong
 */
@SuppressLint("DrawAllocation")
public class RadarView extends View {
	private int w, h;// 获取控件宽高
	private Paint mPaintLine;// 画雷达圆线
	private Paint mPaintSolid;// 画雷达渐变实心圆
	private Matrix matrix;
	private int degrees;
	private Handler mHandler = new Handler();
	private Runnable mRunnable = new Runnable() {
		@Override
		public void run() {
			degrees++;
			matrix.postRotate(degrees, w / 2, h / 2);//旋转矩阵
			RadarView.this.invalidate();// 重绘
			mHandler.postDelayed(mRunnable, 55);
		}
	};

	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);
		setBackgroundResource(R.drawable.radar_bg);//雷达的背景图片(紫色满天星,可以在微信APP中直接找到图片资源)
		initPaint();
		mHandler.postDelayed(mRunnable,500);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		w = getMeasuredWidth();//获取view的宽度
		h = getMeasuredHeight();//获取view的高度
	}

	/**
	 * 初始化画笔
	 */
	private void initPaint() {
		mPaintLine = new Paint();
		mPaintLine.setColor(Color.parseColor("#CCA1A1A1"));// 设置画笔
		mPaintLine.setStrokeWidth(1);// 设置画笔宽度
		mPaintLine.setAntiAlias(true);// 消除锯齿
		mPaintLine.setStyle(Style.STROKE);// 设置空心

		mPaintSolid = new Paint();
		mPaintSolid.setAntiAlias(true);// 消除锯齿
		mPaintSolid.setStyle(Style.FILL);//实心圆
		matrix = new Matrix();//创建组件
	}

	@Override
	protected void onDraw(Canvas canvas) {
		//四个空心圆
		canvas.drawCircle(w / 2, h / 2, w / 6, mPaintLine);
		canvas.drawCircle(w / 2, h / 2, 5 * w / 14, mPaintLine);
		canvas.drawCircle(w / 2, h / 2, 12 * w / 20, mPaintLine);
		canvas.drawCircle(w / 2, h / 2, 9 * w / 11, mPaintLine);
		
		//渐变
		Shader mShader = new SweepGradient(w / 2, h / 2, Color.TRANSPARENT, Color.parseColor("#33FFFFFF"));
		mPaintSolid.setShader(mShader);
		canvas.setMatrix(matrix);
		canvas.drawCircle(w / 2, h / 2, 9 * w / 11, mPaintSolid);
		matrix.reset();//重置矩阵,避免累加,越转越快
		super.onDraw(canvas);
	}

}

本以为微信也是这么实现的,结果发现透明还是照微信差一点点,继续看微信apk解压的资源文件发现,原来连四个空心圆+实心渐变圆是一张图片,害的我还画了半天调比例。所以就有下面的第二种方案。


方案二实现思路(适用于最大圆直径小于屏幕宽度的情况):

1.四个空心圆+实心圆为一张图片

2.普通ImageView+xml旋转动画



旋转动画:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <set xmlns:android="http://schemas.android.com/apk/res/android" >  
  3.   
  4.     <rotate  
  5.         android:duration="10000"  
  6.         android:fromDegrees="0"  
  7.         android:pivotX="50%"  
  8.         android:pivotY="50%"  
  9.         android:repeatCount="-1"  
  10.         android:toDegrees="359"/>  
  11.   
  12. </set>  
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <rotate
        android:duration="10000"
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="-1"
        android:toDegrees="359"/>

</set>

repeatCount设置成-1保证循环转动,从0度转到359,duration时长10秒,绕中心点旋转。

加载动画:

  1. // 加载动画  
  2.         Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.radar_rotate_anim);  
  3.         LinearInterpolator lin = new LinearInterpolator();// 匀速旋转  
  4.         rotateAnim.setInterpolator(lin);  
  5.         ImageView radarImage = (ImageView) findViewById(R.id.image_radar);  
  6.         radarImage.startAnimation(rotateAnim);  
// 加载动画
		Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.radar_rotate_anim);
		LinearInterpolator lin = new LinearInterpolator();// 匀速旋转
		rotateAnim.setInterpolator(lin);
		ImageView radarImage = (ImageView) findViewById(R.id.image_radar);
		radarImage.startAnimation(rotateAnim);
ImageView正常布局就行了,这样就齐活了,就可以用了,但是有个问题,不方便设置让最大的圆直径超过屏幕宽度,可以设置 android:scaleType="centerCrop",但是虽然超出了屏幕,布局效果也微信一至了,但是问题就来了,超出后旋转,图片是长方形的,圆图被截取了,所以这种用法适用于最大圆直径小于屏幕宽度的情况。所以虽然简单但美中不足,于是设想将两种方案结合起来,方案三就诞生了。


方案三实现思路(通用):

1.四个空心圆+实心圆为一张图片

2.自定义ImageView

3.利用方案一的方法用矩阵让image转起来

  1. package com.ml512.radarview;  
  2.   
  3. import com.ml512.radardemo.R;  
  4.   
  5. import android.annotation.SuppressLint;  
  6. import android.content.Context;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.Matrix;  
  9. import android.os.Handler;  
  10. import android.util.AttributeSet;  
  11. import android.widget.ImageView;  
  12. /** 
  13.  * 2015/12/06 22:49 
  14.  * @author ITjianghuxiaoxiong 
  15.  * http://blog.csdn.net/itjianghuxiaoxiong 
  16.  */  
  17. @SuppressLint("DrawAllocation")  
  18. public class RadarImageView extends ImageView {  
  19.     private int w, h;// 获取控件宽高  
  20.     private Matrix matrix;  
  21.     private int degrees;  
  22.     private Handler mHandler = new Handler();  
  23.     private Runnable mRunnable = new Runnable() {  
  24.         @Override  
  25.         public void run() {  
  26.             degrees++;  
  27.             matrix.postRotate(degrees, w / 2, h / 2);  
  28.             RadarImageView.this.invalidate();// 重绘  
  29.             mHandler.postDelayed(mRunnable, 50);  
  30.         }  
  31.     };  
  32.   
  33.     public RadarImageView(Context context) {  
  34.         this(context, null);  
  35.     }  
  36.   
  37.     public RadarImageView(Context context, AttributeSet attrs) {  
  38.         this(context, attrs, 0);  
  39.     }  
  40.   
  41.     public RadarImageView(Context context, AttributeSet attrs, int defStyleAttr) {  
  42.         super(context, attrs, defStyleAttr);  
  43.         init();  
  44.     }  
  45.   
  46.     @Override  
  47.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  48.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  49.         w = getMeasuredWidth();//获取view的宽度  
  50.         h = getMeasuredHeight();//获取view的高度  
  51.     }  
  52.   
  53.     /** 
  54.      * 初始化 
  55.      */  
  56.     private void init() {  
  57.         setBackgroundResource(R.drawable.radar_bg);  
  58.         matrix = new Matrix();  
  59.         mHandler.postDelayed(mRunnable,500);  
  60.     }  
  61.   
  62.     @Override  
  63.     protected void onDraw(Canvas canvas) {  
  64.         canvas.setMatrix(matrix);  
  65.         super.onDraw(canvas);  
  66.         matrix.reset();  
  67.     }  
  68.   
  69. }  
package com.ml512.radarview;

import com.ml512.radardemo.R;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.os.Handler;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
 * 2015/12/06 22:49
 * @author ITjianghuxiaoxiong
 * http://blog.csdn.net/itjianghuxiaoxiong
 */
@SuppressLint("DrawAllocation")
public class RadarImageView extends ImageView {
	private int w, h;// 获取控件宽高
	private Matrix matrix;
	private int degrees;
	private Handler mHandler = new Handler();
	private Runnable mRunnable = new Runnable() {
		@Override
		public void run() {
			degrees++;
			matrix.postRotate(degrees, w / 2, h / 2);
			RadarImageView.this.invalidate();// 重绘
			mHandler.postDelayed(mRunnable, 50);
		}
	};

	public RadarImageView(Context context) {
		this(context, null);
	}

	public RadarImageView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public RadarImageView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init();
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		w = getMeasuredWidth();//获取view的宽度
		h = getMeasuredHeight();//获取view的高度
	}

	/**
	 * 初始化
	 */
	private void init() {
		setBackgroundResource(R.drawable.radar_bg);
		matrix = new Matrix();
		mHandler.postDelayed(mRunnable,500);
	}

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

}
布局引用:

  1. <com.ml512.radarview.RadarImageView  
  2.         android:id="@+id/image_radar"  
  3.         android:layout_width="match_parent"  
  4.         android:layout_height="match_parent"   
  5.         android:scaleType="centerCrop"  
  6.         android:padding="15dp"  
  7.         android:src="@drawable/wx_radar_imgae"/>  
<com.ml512.radarview.RadarImageView
        android:id="@+id/image_radar"
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        android:scaleType="centerCrop"
        android:padding="15dp"
        android:src="@drawable/wx_radar_imgae"/>

很简单吧,好了,以上就是三种实现方案了,根据自己的实际需要选择吧,同时如果有更好的解决方法,也欢迎交流~

Demo源码 :http://download.csdn.NET/detail/itjianghuxiaoxiong/9331925

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值