雷达扫描之自定义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();//刷新界面
			
			
		}
		
	}

}


布局就不在重新贴了, 上面有,  谢谢。



  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值