1. 程式人生 > >Android UI 之自定義RadarView——高仿微信雷達掃描

Android UI 之自定義RadarView——高仿微信雷達掃描

最近看了一個視訊講了一種微信雷達掃描的實現方案,借鑑了一下,自己也寫一個玩玩,與大家分享一下。基本想出來三種解決方案,根據不同需求情況選擇即可。


方案一實現思路(通用):

1.自定義view 

2.重寫onDraw()方法

3.畫四個無鋸齒空心圓

4.畫以最大圓為半徑的實心漸變圓

5.建立矩陣,旋轉畫布,重繪,並用Handler實現迴圈

[java] view plain copy print?
  1. package com.ml512.radarview;  
  2. import com.ml512.radardemo.R;  
  3. import android.annotation.SuppressLint;  
  4. import android.content.Context;  
  5. import android.graphics.Canvas;  
  6. import android.graphics.Color;  
  7. import android.graphics.Matrix;  
  8. import android.graphics.Paint;  
  9. import android.graphics.Paint.Style;  
  10. import android.graphics.Shader;  
  11. import android.graphics.SweepGradient;  
  12. import android.os.Handler;  
  13. import android.util.AttributeSet;  
  14. import android.view.View;  
  15. /** 
  16.  * 2015/12/06 22:49 
  17.  * @author ITjianghuxiaoxiong 
  18.  * http://blog.csdn.net/itjianghuxiaoxiong 
  19.  */
  20. @SuppressLint("DrawAllocation")  
  21. publicclass RadarView extends View {  
  22.     privateint w, h;// 獲取控制元件寬高
  23.     private Paint mPaintLine;// 畫雷達圓線
  24.     private Paint mPaintSolid;
    // 畫雷達漸變實心圓
  25.     private Matrix matrix;  
  26.     privateint degrees;  
  27.     private Handler mHandler = new Handler();  
  28.     private Runnable mRunnable = new Runnable() {  
  29.         @Override
  30.         publicvoid run() {  
  31.             degrees++;  
  32.             matrix.postRotate(degrees, w / 2, h / 2);//旋轉矩陣
  33.             RadarView.this.invalidate();// 重繪
  34.             mHandler.postDelayed(mRunnable, 55);  
  35.         }  
  36.     };  
  37.     public RadarView(Context context) {  
  38.         this(context, null);  
  39.     }  
  40.     public RadarView(Context context, AttributeSet attrs) {  
  41.         this(context, attrs, 0);  
  42.     }  
  43.     public RadarView(Context context, AttributeSet attrs, int defStyleAttr) {  
  44.         super(context, attrs, defStyleAttr);  
  45.         setBackgroundResource(R.drawable.radar_bg);//雷達的背景圖片(紫色滿天星,可以在微信APP中直接找到圖片資源)
  46.         initPaint();  
  47.         mHandler.postDelayed(mRunnable,500);  
  48.     }  
  49.     @Override
  50.     protectedvoid onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  51.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  52.         w = getMeasuredWidth();//獲取view的寬度
  53.         h = getMeasuredHeight();//獲取view的高度
  54.     }  
  55.     /** 
  56.      * 初始化畫筆 
  57.      */
  58.     privatevoid initPaint() {  
  59.         mPaintLine = new Paint();  
  60.         mPaintLine.setColor(Color.parseColor("#CCA1A1A1"));// 設定畫筆
  61.         mPaintLine.setStrokeWidth(1);// 設定畫筆寬度
  62.         mPaintLine.setAntiAlias(true);// 消除鋸齒
  63.         mPaintLine.setStyle(Style.STROKE);// 設定空心
  64.         mPaintSolid = new Paint();  
  65.         mPaintSolid.setAntiAlias(true);// 消除鋸齒
  66.         mPaintSolid.setStyle(Style.FILL);//實心圓
  67.         matrix = new Matrix();//建立元件
  68.     }  
  69.     @Override
  70.     protectedvoid onDraw(Canvas canvas) {  
  71.         //四個空心圓
  72.         canvas.drawCircle(w / 2, h / 2, w / 6, mPaintLine);  
  73.         canvas.drawCircle(w / 2, h / 25 * w / 14, mPaintLine);  
  74.         canvas.drawCircle(w / 2, h / 212 * w / 20, mPaintLine);  
  75.         canvas.drawCircle(w / 2, h / 29 * w / 11, mPaintLine);  
  76.         //漸變
  77.         Shader mShader = new SweepGradient(w / 2, h / 2, Color.TRANSPARENT, Color.parseColor("#33FFFFFF"));  
  78.         mPaintSolid.setShader(mShader);  
  79.         canvas.setMatrix(matrix);  
  80.         canvas.drawCircle(w / 2, h / 29 * w / 11, mPaintSolid);  
  81.         matrix.reset();//重置矩陣,避免累加,越轉越快
  82.         super.onDraw(canvas);  
  83.     }  
  84. }  
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旋轉動畫


旋轉動畫:

[html] view plain copy print?
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <setxmlns:android="http://schemas.android.com/apk/res/android">
  3.     <rotate
  4.         android:duration="10000"
  5.         android:fromDegrees="0"
  6.         android:pivotX="50%"
  7.         android:pivotY="50%"
  8.         android:repeatCount="-1"
  9.         android:toDegrees="359"/>
  10. </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秒,繞中心點旋轉。

載入動畫:

[java] view plain copy print?
  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轉起來 [java] view plain copy print?
  1. package com.ml512.radarview;  
  2. import com.ml512.radardemo.R;  
  3. import android.annotation.SuppressLint;  
  4. import android.content.Context;  
  5. import android.graphics.Canvas;  
  6. import android.graphics.Matrix;  
  7. import android.os.Handler;  
  8. import android.util.AttributeSet;  
  9. import android.widget.ImageView;  
  10. /** 
  11.  * 2015/12/06 22:49 
  12.  * @author ITjianghuxiaoxiong 
  13.  * http://blog.csdn.net/itjianghuxiaoxiong 
  14.  */
  15. @SuppressLint("DrawAllocation")  
  16. publicclass RadarImageView extends ImageView {  
  17.     privateint w, h;// 獲取控制元件寬高
  18.     private Matrix matrix;  
  19.     privateint degrees;  
  20.     private Handler mHandler = new Handler();  
  21.     private Runnable mRunnable = new Runnable() {  
  22.         @Override
  23.         publicvoid run() {  
  24.             degrees++;  
  25.             matrix.postRotate(degrees, w / 2, h / 2);  
  26.             RadarImageView.this.invalidate();// 重繪
  27.             mHandler.postDelayed(mRunnable, 50);  
  28.         }  
  29.     };  
  30.     public RadarImageView(Context context) {  
  31.         this(context, null);  
  32.     }  
  33.     public RadarImageView(Context context, AttributeSet attrs) {  
  34.         this(context, attrs, 0);  
  35.     }  
  36.     public RadarImageView(Context context, AttributeSet attrs, int defStyleAttr) {  
  37.         super(context, attrs, defStyleAttr);  
  38.         init();  
  39.     }  
  40.     @Override
  41.     protectedvoid onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  42.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  43.         w = getMeasuredWidth();//獲取view的寬度
  44.         h = getMeasuredHeight();//獲取view的高度
  45.     }  
  46.     /** 
  47.      * 初始化 
  48.      */
  49.     privatevoid init() {  
  50.         setBackgroundResource(R.drawable.radar_bg);  
  51.         matrix = new Matrix();  
  52.         mHandler.postDelayed(mRunnable,500);  
  53.     }  
  54.     @Override
  55.     protectedvoid onDraw(Canvas canvas) {  
  56.         canvas.setMatrix(matrix);  
  57.         super.onDraw(canvas);  
  58.         matrix.reset();  
  59.     }  
  60. }  
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();
	}

}
佈局引用: [html] view plain copy print?
  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