雷達掃描之自定義view,打造最簡單的自定義View

 前言:關於自定view , 相信很多人都知道 自定義步驟 onMeasure-onLayout-onDraw  , 但是自己能隨手魯出一個,相信很多人束手無措,記得很久以前在騰訊課堂**學院晚上聽課, 講了一個微信的雷達掃描, 當時覺得很難, 無法理解, 這幾天一直在看View ViewGroup源碼, 突然想起 順手寫了把, 和大家分享下。。。先看圖


  

   XML文件中簡單佈局

   

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000" >

    
    
    
    
    <com.qypt.just_android_wechat_radar.Wechat_radar_view 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/radar"
        />
    
    <ImageView 
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:id="@+id/image"
        android:layout_gravity="center"/>
    
</FrameLayout>
    

    佈局很簡單,就一個FrameLayout 嵌套着自定義 雷達掃描佈局,和一個頭像,這裏需要 頭像壓在自定佈局上面, 因爲跟上面的自定義佈局的實現有關.詳細請繼續看

  

/**
	 * 定於全局Context
	 */
	private Context context;
	/**
	 * 畫筆, 用於畫圓圈
	 */
	private Paint mPaint;
	/**
	 * 頭像的寬度
	 */
	private int width;
	/**
	 * 頭像的高度
	 */
	private int height;
	/**
	 * 圓心的X,Y座標
	 */
	private int pointX,pointY;
	/**
	 * 最裏層的圓半徑, 最小那個圓的半徑
	 */
	private int minRadius;
	/**
	 * 圓與圓之間半徑只差
	 */
	private static int ADD=60;
	//圓的個數
	private static  int CIRCLE_NUMBER=5;
	//矩陣 用於旋轉圓
	private Matrix mMatrix;
	//旋轉角度
	private float degree=0;
<span style="white-space:pre">	</span>//控制漸變圓的繪製
	private boolean isStart=true;
	public Wechat_radar_view(Context context) {
		this(context, null);
		// TODO Auto-generated constructor stub
	}

	public Wechat_radar_view(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
		// TODO Auto-generated constructor stub
	}

	public Wechat_radar_view(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		this.context = context;
		init();
	}
        新建了一個類,命名爲Wechat_radar_view extends View  implements Runnable繼承View 實現Runnable接口,定義變量 和構造方法,每一個變量都有註釋了, 這裏不多做解釋,  構造方法,一般的做法都是,第一個調用第二個,第二個去調用第三個。。。 我也是從源碼學來的;

      看看init()吧, 看看究竟初始化了什麼


    

private void init() {

		mPaint = new Paint();
		mPaint.setAntiAlias(true);//抗鋸齒
		mPaint.setDither(true);//設置抖動, 使畫出來的東西更加平滑,清晰
		mPaint.setStyle(Style.STROKE);//畫筆爲空心
		mPaint.setColor(Color.parseColor("#CCA1A1A1"));//設置畫筆顏色
 		this.setLayerType(LAYER_TYPE_SOFTWARE, null);//關閉硬件加速

		/**
		 * 獲取半徑
		 */
		TypedValue tv = new TypedValue();
		width = (int) tv.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120,
				context.getResources().getDisplayMetrics());
		height = width;   //這裏寬高是頭像的寬高
		
		ADD=(int) tv.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60,
				context.getResources().getDisplayMetrics()); //兩圓之間的差 60dp  默認
		if (width == 0 || width == -1) {
			width = 240;
			height = 240;
			Log.i("Info", "obtain value fail");
		}

		minRadius=width/2;  
		tv = null;
		
		
		int mScreenHeight=this.getResources().getDisplayMetrics().heightPixels;
		CIRCLE_NUMBER=(mScreenHeight-width)/2/ADD+3; //計算圓的個數 , 這裏多加兩個原因是畫滿全屏看起來舒服寫
		
		
		
		mMatrix = new Matrix(); 
		
		
	}

  可以看到初始化onDraw()方法中所需要用的東西。  上面都是爲onDraw方法準備東西, 既然準備好了, 我們就正式開始繪製我們的雷達掃描吧

<pre name="code" class="java">/**
	 * 繪製雷達
	 */
	@Override
	protected void onDraw(Canvas canvas) {

		int view_Width=this.getWidth(); //獲取View的寬度
		int view_Height=this.getHeight();//獲取View的高度
		pointX=view_Width/2;//圓心的X座標
		pointY=view_Height/2;//圓心的Y座標
		/**
		 * 一個For循環把所有的空心圓畫出來
		 */
		for(int i=0;i<CIRCLE_NUMBER;i++)
		{
			
			canvas.drawCircle(pointX, pointY, minRadius+(i*ADD), mPaint);
		}
		//設置畫筆顏色的漸變
		Shader mShader = new SweepGradient(view_Width/2, view_Height / 2, Color.GRAY, Color.parseColor("#10FFFFFF"));
		mPaint.setShader(mShader);
		mPaint.setStyle(Style.FILL); //把畫筆設置成實心
		canvas.setMatrix(mMatrix);  //設置矩陣
		canvas.drawCircle(pointX, pointY, (minRadius+(CIRCLE_NUMBER*ADD)), mPaint); //畫掃描圓
		/**
		 * 恢復下畫筆和重置矩陣
		 */
		mPaint.setShader(null);
		mPaint.setStyle(Style.STROKE);
		mMatrix.reset();
	}


      就這麼簡單就繪製完成我們的雷達掃描,  下面主要完成任務, 要控制他的動,和變化了,這裏的動,主要通過不斷的重繪我們的UI實現, 這裏爲了控制的視圖的生命週期,用了 子線程, 而不是直接在onDraw方法調用this.invalidate()不斷的遞歸回調,(也可以實現 不過不建議)  看看 Wechat_radar_view 的Run方法

 

@Override
	public void run() {
		
		while (isStart) {
			
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			mMatrix.setRotate(--degree, this.getWidth()/2, this.getHeight()/2); //設置矩陣角度
			postInvalidate();//刷新界面
			
			
		}
		
	}

      先讓線程睡100ms,不然轉的太快, 然後設置矩陣,開始繪製; 就這麼簡單完成了我們自定義View, 其實這只是自定於View的冰山一角,因爲自定View不單單這些, 有了它我們可以做出千變萬化的View。今天我們的View很簡單隻重寫了onDraw方法



/*******************************************解析完畢*************************************************/



因爲代碼不多, 就把所有代碼貼出來了

 

/**
 * 
 * @author Administrator justson
 *
 */
public class MainActivity extends ActionBarActivity {

	private Wechat_radar_view radar;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.activity_main);
		
		ImageView image=(ImageView) this.findViewById(R.id.image);
		image.setImageBitmap(BitmapUtils.circleBitmap(BitmapUtils.getProcessBitmap(R.drawable.a, this, 120, 120)));
		
		radar = (Wechat_radar_view) this.findViewById(R.id.radar);
		
		
		
	}
	@Override
	protected void onResume() {
		Thread mThread=new Thread(radar);
		radar.setStart(true);
		mThread.start();
		super.onResume();
	}

	@Override
	protected void onPause() {
		super.onPause();
		radar.setStart(false);
	}
	 
}

  下面的是圖片處理類, 用來壓縮圖片和畫圓角圖片

/**
 * 
 * @author Administrator  justson
 *
 */
public class BitmapUtils {

	
	public static Bitmap  getProcessBitmap(int resId,Context context, int width_dp,int height_dp)
	{
		if(resId==0||context==null)
			return null;
		
		BitmapFactory.Options options=new BitmapFactory.Options();
		options.inJustDecodeBounds=true;
		BitmapFactory.decodeResource(context.getResources(), resId, options);
		int pWidth=options.outWidth;
		int pHeight=options.outHeight;
		int rate=getRate(width_dp,height_dp,pWidth,pHeight,context);
		
		options.inPurgeable=true;
		options.inDither=true;
		options.inJustDecodeBounds=false;
		Bitmap bitmap=BitmapFactory.decodeResource(context.getResources(), resId, options);
		
		return bitmap;
	}

	public static Bitmap circleBitmap(Bitmap bitmap) {
		if(bitmap==null)
		{
			return null;	
		}
			
			
		int radius=Math.min(bitmap.getHeight(), bitmap.getWidth())/2;
		Log.i("Info", "radius:"+radius);
		Bitmap cBitmap=Bitmap.createBitmap(radius*2, radius*2, Config.ARGB_8888);
		Canvas canvas =new Canvas(cBitmap);
		RectF r=new RectF(0, 0, cBitmap.getWidth(), cBitmap.getHeight());
		Paint mPaint=new Paint();
		mPaint.setAntiAlias(true);
		mPaint.setDither(true);
		mPaint.setColor(Color.BLACK);
		mPaint.setStyle(Style.FILL);
		canvas.drawCircle(cBitmap.getWidth()/2, cBitmap.getHeight()/2	, radius, mPaint);
		mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
		canvas.drawBitmap(bitmap, null, r, mPaint);
	
		if(bitmap!=null){
			bitmap.recycle();
			bitmap=null;
		}
		return cBitmap;
	}

	private static int getRate(int width_dp, int height_dp, int pWidth,
			int pHeight,Context context) {
		
		int rate=1;
		int cWidth=(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, width_dp, context.getResources().getDisplayMetrics());
		int cHeight=(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height_dp, context.getResources().getDisplayMetrics());
		if(cWidth>pWidth||cHeight>pHeight){
			rate=Math.max(pWidth/cWidth, pHeight/cHeight);
			
		}
		
		return rate;
	}
	
	

//自定義View類(雷達掃描View)

public class Wechat_radar_view extends View  implements Runnable{

	/**
	 * 定於全局Context
	 */
	private Context context;
	/**
	 * 畫筆, 用於畫圓圈
	 */
	private Paint mPaint;
	/**
	 * 頭像的寬度
	 */
	private int width;
	/**
	 * 頭像的高度
	 */
	private int height;
	/**
	 * 圓心的X,Y座標
	 */
	private int pointX,pointY;
	/**
	 * 最裏層的圓半徑, 最小那個圓的半徑
	 */
	private int minRadius;
	/**
	 * 圓與圓之間半徑只差
	 */
	private static int ADD=60;
	//圓的個數
	private static  int CIRCLE_NUMBER=5;
	//矩陣 用於旋轉圓
	private Matrix mMatrix;
	//旋轉角度
	private float degree=0;

	private boolean isStart=true;
	public Wechat_radar_view(Context context) {
		this(context, null);
		// TODO Auto-generated constructor stub
	}

	public Wechat_radar_view(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
		// TODO Auto-generated constructor stub
	}

	public Wechat_radar_view(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		this.context = context;
		init();
	}

	/**
	 * 初始化畫筆
	 */
	private void init() {

		mPaint = new Paint();
		mPaint.setAntiAlias(true);//抗鋸齒
		mPaint.setDither(true);//設置抖動, 使畫出來的東西更加平滑,清晰
		mPaint.setStyle(Style.STROKE);//畫筆爲空心
		mPaint.setColor(Color.parseColor("#CCA1A1A1"));//設置畫筆顏色
 		this.setLayerType(LAYER_TYPE_SOFTWARE, null);//關閉硬件加速

		/**
		 * 獲取半徑
		 */
		TypedValue tv = new TypedValue();
		width = (int) tv.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120,
				context.getResources().getDisplayMetrics());
		height = width;   //這裏寬高是頭像的寬高
		
		ADD=(int) tv.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60,
				context.getResources().getDisplayMetrics()); //兩圓之間的差 60dp  默認
		if (width == 0 || width == -1) {
			width = 240;
			height = 240;
			Log.i("Info", "obtain value fail");
		}

		minRadius=width/2;  
		tv = null;
		
		
		int mScreenHeight=this.getResources().getDisplayMetrics().heightPixels;
		CIRCLE_NUMBER=(mScreenHeight-width)/2/ADD+3; //計算圓的個數 , 這裏多加兩個原因是畫滿全屏看起來舒服寫
		
		
		
		mMatrix = new Matrix(); 
		
		
	}

	/**
	 * 繪製雷達
	 */
	@Override
	protected void onDraw(Canvas canvas) {

		int view_Width=this.getWidth(); //獲取View的寬度
		int view_Height=this.getHeight();//獲取View的高度
		pointX=view_Width/2;//圓心的X座標
		pointY=view_Height/2;//圓心的Y座標
		/**
		 * 一個For循環把所有的空心圓畫出來
		 */
		for(int i=0;i<CIRCLE_NUMBER;i++)
		{
			
			canvas.drawCircle(pointX, pointY, minRadius+(i*ADD), mPaint);
		}
		//設置畫筆顏色的漸變
		Shader mShader = new SweepGradient(view_Width/2, view_Height / 2, Color.GRAY, Color.parseColor("#10FFFFFF"));
		mPaint.setShader(mShader);
		mPaint.setStyle(Style.FILL); //把畫筆設置成實心
		canvas.setMatrix(mMatrix);  //設置矩陣
		canvas.drawCircle(pointX, pointY, (minRadius+(CIRCLE_NUMBER*ADD)), mPaint); //畫掃描圓
		/**
		 * 恢復下畫筆和重置矩陣
		 */
		mPaint.setShader(null);
		mPaint.setStyle(Style.STROKE);
		mMatrix.reset();
	}

	
	public boolean isStart() {
		return isStart;
	}

	public void setStart(boolean isStart) {
		this.isStart = isStart;
	}

	@Override
	public void run() {
		
		while (isStart) {
			
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			mMatrix.setRotate(--degree, this.getWidth()/2, this.getHeight()/2); //設置舉證角度
			postInvalidate();//刷新界面
			
			
		}
		
	}

}


佈局就不在重新貼了, 上面有,  謝謝。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章