自定义View之继承View(圆形进度图,播放器条形图)

客官°小女子只卖身不卖艺 2022-08-21 04:41 218阅读 0赞

重写View来实现全新的控件

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

首先看一下实例图![
20160315205820373

圆形进度图

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

  • 首先看一下我们定义的一些字段

    /* 圆心坐标 */

    1. private int mCircleXY;
    2. /** * 内部圆半径 */
    3. private int mRadius;
    4. /** * 控件的宽度 */
    5. private int width;
    6. /** * 椭圆的文字 */
    7. private String mText;
    8. /** * 弧形的画笔 */
    9. private Paint mArcPaint;
    10. /** * 文字的画笔 */
    11. private Paint mTextPaint;
    12. /** * 内部圆的画笔 */
    13. private Paint mCirclePaint;
  1. /** * 弧形的内切矩形 */
  2. private Rect mArcRect;
  3. /** * 圆心文字 */
  4. private String mCenterText = "Alex_Mahao";
  5. /** * 文字所占大小 */
  6. private Rect mTextBound = new Rect();
  7. /** * 外部弧形的度数 */
  8. private int mSweepAngle;
  9. /** * 外部弧形的最终度数 */
  10. private int mEndAngle;
  • 重写onMeasure()方法。

    @Override

    1. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    2. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    3. width = Math.min(getMeasuredWidth(),getMeasuredHeight());
    4. setMeasuredDimension(width,width);
    5. mCircleXY = width/2;
    6. mRadius = width/2/2;
    7. initPaint();
    8. //弧形矩形的范围
    9. 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);
    10. //测量文字的大小
    11. mTextPaint.getTextBounds(mCenterText, 0, mCenterText.length(), mTextBound);
    12. //设置弧形的进度
    13. setProgress(270);
    14. }

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

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

  • 初始化画笔

    private void initPaint() {

    1. mArcPaint = new Paint();
    2. mArcPaint.setColor(Color.BLUE);
    3. mArcPaint.setAntiAlias(true);
    4. mArcPaint.setStyle(Paint.Style.STROKE);
    5. mArcPaint.setStrokeWidth(width/2/2/2);
  1. mCirclePaint = new Paint();
  2. mCirclePaint.setColor(Color.RED);
  3. mCirclePaint.setStyle(Paint.Style.FILL);
  4. mCirclePaint.setAntiAlias(true);
  5. mTextPaint = new Paint();
  6. mTextPaint.setColor(Color.WHITE);
  7. mTextPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,16,getResources().getDisplayMetrics()));
  8. mTextPaint.setAntiAlias(true);
  9. }

在该方法中,我们初始化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

    1. protected void onDraw(Canvas canvas) {
    2. canvas.drawCircle(mCircleXY,mCircleXY,mRadius,mCirclePaint);
    3. canvas.drawText(mCenterText,width/2-mTextBound.width()/2,getHeight()/2+mTextBound.height()/2,mTextPaint);
    4. //在画笔有宽度的情况下,画笔的宽度的一半正好内切范围矩形
    5. canvas.drawArc(new RectF(mArcRect),-90,mSweepAngle,false,mArcPaint);
    6. if(mEndAngle>mSweepAngle){
    7. mSweepAngle++;
    8. postInvalidateDelayed(5);
    9. }
  1. }

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

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

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

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

  1. public void setProgress(int endAngle){
  2. mEndAngle = endAngle;
  3. mSweepAngle = 0;
  4. postInvalidate();
  5. }

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

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

条形图(音乐播放的)

按照惯例,先上字段:

  1. /** * 控件的宽度 */
  2. private int mWidth;
  3. /** * 条形的高度 */
  4. private int mRectHeight;
  5. /** * 条形的宽度 */
  6. private int mRectWidth;
  7. /** * 条形的数量 */
  8. private int mRectCount = 5;
  9. /** * 颜色的渲染器 */
  10. private LinearGradient mLinearGradient;
  11. /** * 画笔 */
  12. private Paint mPaint;
  13. /** * 随机条形的高度 */
  14. private double mRandom;
  • 在构造方法中初始化画笔

    /* 初始化画笔 */

    1. mPaint = new Paint();
    2. mPaint.setStyle(Paint.Style.FILL);
    3. mPaint.setAntiAlias(true);
  • 在onSizeChange()方法中,获取一些必要属性

    //控件的宽度

    1. mWidth = getWidth();
    2. //条形的最大高度,最低端的坐标
    3. mRectHeight = getHeight();
    4. //条形图占总宽度的位置
    5. mRectWidth = (int) (mWidth*0.6/mRectCount);
    6. //渲染器,
    7. mLinearGradient = new LinearGradient(0,0,mRectWidth,mRectHeight, Color.YELLOW,Color.BLUE, Shader.TileMode.CLAMP);
    8. //设置渲染器
    9. mPaint.setShader(mLinearGradient);
  • onDraw()中画出条形图。

    for(int i=0;i<mRectCount;i++){

    1. //获取随机数,获得条形的高度
    2. mRandom = Math.random();
    3. float currentHeight = (float) (mRectHeight*mRandom);
    4. //画出条形图
    5. canvas.drawRect((float)(mWidth*0.4/2+mRectWidth*i),
    6. currentHeight,
    7. (float)( mWidth*0.4/2+mRectWidth*(i+1)),
    8. mRectHeight,
    9. mPaint
    10. );
    11. }
    12. //300ms刷新视图,改变条形图
    13. 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

发表评论

表情:
评论列表 (有 0 条评论,218人围观)

还没有评论,来说两句吧...

相关阅读