重写View来实现全新的控件

在Android中重写View是Android中的难点,但很多特效都是基于自定义View来实现的,下面我们来尝试通过两个例子来学习一下自定义View。

首先看一下实例图![

圆形进度图

通过看图,整个图可以分为三部分,内部圆环,外部弧形,以及文字。

  • 首先看一下我们定义的一些字段
  /** * 圆心坐标 */    private int mCircleXY;    /** * 内部圆半径 */    private int mRadius;    /** * 控件的宽度 */    private int width;    /** * 椭圆的文字 */    private String mText;    /** * 弧形的画笔 */    private Paint mArcPaint;    /** * 文字的画笔 */    private Paint mTextPaint;    /** * 内部圆的画笔 */    private Paint mCirclePaint;    /** * 弧形的内切矩形 */    private Rect mArcRect;    /** * 圆心文字 */    private String mCenterText = "Alex_Mahao";    /** * 文字所占大小 */    private Rect mTextBound = new Rect();    /** * 外部弧形的度数 */    private int mSweepAngle;    /** * 外部弧形的最终度数 */    private int mEndAngle;
  • 重写onMeasure()方法。
 @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        width = Math.min(getMeasuredWidth(),getMeasuredHeight());        setMeasuredDimension(width,width);        mCircleXY = width/2;        mRadius = width/2/2;        initPaint();        //弧形矩形的范围        mArcRect = new Rect(width/2/2/2/2,width/2/2/2/2,width-width/2/2/2/2,width-width/2/2/2/2);        //测量文字的大小        mTextPaint.getTextBounds(mCenterText, 0, mCenterText.length(), mTextBound);        //设置弧形的进度        setProgress(270);    }

在onMeasure()方法中,首先对比宽和高取最小值,因为我们的该View为正方形,在第6行,我们获取测量后的宽高,取最小值,并重新设置控件的宽高。

然后我们获取该控件的中心坐标。并获取内部圆的半径为控件宽度的四分之一。

  • 初始化画笔
  private void initPaint() {        mArcPaint = new Paint();        mArcPaint.setColor(Color.BLUE);        mArcPaint.setAntiAlias(true);        mArcPaint.setStyle(Paint.Style.STROKE);        mArcPaint.setStrokeWidth(width/2/2/2);        mCirclePaint = new Paint();        mCirclePaint.setColor(Color.RED);        mCirclePaint.setStyle(Paint.Style.FILL);        mCirclePaint.setAntiAlias(true);        mTextPaint = new Paint();        mTextPaint.setColor(Color.WHITE);        mTextPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,16,getResources().getDisplayMetrics()));        mTextPaint.setAntiAlias(true);    }

在该方法中,我们初始化mArcPaint,mCirclePaint,mTextPaint,在设置mArcPaint中设置了Paint的画笔宽度,用来画出圆形弧,在设置了画笔的宽度之后,我们的onMeasure()方法中,初始化弧形矩形的范围,那圆形弧的宽度的一半内切矩形,所以,我们必须减去弧形矩形的一半。及弧形的宽度为width/2/2/2,则对应的内切矩形为(width/2/2/2/2,width/2/2/2/2,width-width/2/2/2/2,width-width/2/2/2/2);

  • 重写onDraw()方法,画出对应图形。
 @Override    protected void onDraw(Canvas canvas) {        canvas.drawCircle(mCircleXY,mCircleXY,mRadius,mCirclePaint);        canvas.drawText(mCenterText,width/2-mTextBound.width()/2,getHeight()/2+mTextBound.height()/2,mTextPaint);        //在画笔有宽度的情况下,画笔的宽度的一半正好内切范围矩形        canvas.drawArc(new RectF(mArcRect),-90,mSweepAngle,false,mArcPaint);        if(mEndAngle>mSweepAngle){            mSweepAngle++;            postInvalidateDelayed(5);        }    }

通过canvas.drawCircle()方法画出内部圆,参数分别为圆心x坐标,圆心y坐标,半径,画笔。

通过canvas.drawText()画出文字,因为我们要将文字画出到中心,在onMeasure()方法通过Paint的getTextBounds()测量出包含文字所占空间大小的Rect对象,继而通过控件宽高的一半-Rect对象的一半,设置到对应中心。

通过canvas.drawArc()方法画出弧形,参数分别为,内切矩形,起始度数(0都为右侧,-90度为上方),弧度的偏移度数,是否画出圆心,画笔。

如果我们不想外部弧度慢慢增加,直接将mSweepAngle设置为固定值即可。如果我们想要其慢慢滚动,有一个缓冲效果,我们需要添加如下方法

 public void setProgress(int endAngle){        mEndAngle = endAngle;        mSweepAngle = 0;        postInvalidate();    }

该方法,可以在activity中调用,我为了简单,在onMeasure()方法中调用了,该方法做了三件事,将我们最终的弧度赋值,将当前弧度置为0,刷新控件(就是调用onDraw())方法。那么,在onDraw()方法中,会进入到if(mEndAngle>mSweepAngle)中,自增当前度数,同时发送一个5ms之后刷新试图的延时任务。

  • OK,搞定,我们可以根据自己的需求,定制内部的文字,颜色等等。

条形图(音乐播放的)

按照惯例,先上字段:

       /** * 控件的宽度 */    private int mWidth;    /** * 条形的高度 */    private int mRectHeight;    /** * 条形的宽度 */    private int mRectWidth;    /** * 条形的数量 */    private int mRectCount = 5;    /** * 颜色的渲染器 */    private LinearGradient mLinearGradient;    /** * 画笔 */    private Paint mPaint;    /** * 随机条形的高度 */    private double mRandom;
  • 在构造方法中初始化画笔
        /** * 初始化画笔 */        mPaint = new Paint();        mPaint.setStyle(Paint.Style.FILL);        mPaint.setAntiAlias(true);
  • 在onSizeChange()方法中,获取一些必要属性
        //控件的宽度        mWidth = getWidth();        //条形的最大高度,最低端的坐标        mRectHeight = getHeight();        //条形图占总宽度的位置        mRectWidth = (int) (mWidth*0.6/mRectCount);        //渲染器,        mLinearGradient = new LinearGradient(0,0,mRectWidth,mRectHeight, Color.YELLOW,Color.BLUE, Shader.TileMode.CLAMP);        //设置渲染器        mPaint.setShader(mLinearGradient);
  • onDraw()中画出条形图。
   for(int i=0;i<mRectCount;i++){            //获取随机数,获得条形的高度            mRandom = Math.random();            float currentHeight = (float) (mRectHeight*mRandom);            //画出条形图            canvas.drawRect((float)(mWidth*0.4/2+mRectWidth*i),                        currentHeight,                    (float)( mWidth*0.4/2+mRectWidth*(i+1)),                        mRectHeight,                        mPaint                    );        }        //300ms刷新视图,改变条形图        postInvalidateDelayed(300);

注释已经很清楚了,不再多解释。

下面对这两个控件的知识点进行总结

总结
1. 在通过canvas.drawArc()画圆弧,设置了画笔的宽度,则限制圆弧的外切矩形,外切的不是圆弧最外端的像素,而是画笔宽度的一半,所以此时在计算外切矩形时,需要考虑到画笔的宽度。
2. 让文字居中,一直是自定义View中头疼的一件事,我们通过Paint的getTextBounds(String text, int start,int end,Rect bounds)方法,获取文字所占空间大小,该控件值之间存储到了传入的bounds参数中,这时,我们通过canvas.drawText(String text,float x,float y,Paint paint);这里又有一个坑,x表示的是左上x的坐标,y是右下y的坐标。
3. TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,16,getResources().getDisplayMetrics()),sp->dp的转化

关于自定义View的源码已上传到github。如需源码请移步https://github.com/AlexSmille/CustomView

更多相关文章

  1. Android(安卓)Studio——Android(安卓)View 如何绘制
  2. Android(安卓)TextView大全
  3. 字节跳动屏幕适配方案解读
  4. Android中常见的热门标签的流式布局的实现
  5. Android中Canvas类的介绍
  6. Android屏幕密度(Density)和分辨率概念详解
  7. Android之文字描边
  8. 2012-7-18 Android(安卓)的Paint(画笔)及Canvas(画布)
  9. Android(安卓)组件宽度高度自适应

随机推荐

  1. Android ApiDemos示例解析(49):Content->R
  2. Android权限管理之RxPermission解决Andro
  3. Android开发学习笔记(五)Android五大布局
  4. Android AdbCommandRejectedException和c
  5. QT for Android HelloWorld.apk!耶
  6. Android中部署自己的su
  7. 【Android】gradle使用过程的问题解决汇
  8. Android Studio安装及常见错误
  9. Android(安卓)知识点积累(一)
  10. 获得G1的电池电量信息