雷达扫描之自定义view,打造最简单的自定义View

来源:互联网 发布:网络高清电视在线直播 编辑:程序博客网 时间:2024/05/21 08:43

 前言:关于自定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">/** * 绘制雷达 */@Overrideprotected 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方法

 

@Overridepublic void run() {while (isStart) {try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.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;@Overrideprotected 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);}@Overrideprotected void onResume() {Thread mThread=new Thread(radar);radar.setStart(true);mThread.start();super.onResume();}@Overrideprotected 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(); }/** * 绘制雷达 */@Overrideprotected 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;}@Overridepublic void run() {while (isStart) {try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}mMatrix.setRotate(--degree, this.getWidth()/2, this.getHeight()/2); //设置举证角度postInvalidate();//刷新界面}}}


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



2 0
原创粉丝点击