android 自定义view控件带图标,进度,文字

#自定义带图标数字圆形进度条
在这里插入图片描述
1.首先新建一个类MySelfView,集成View

public class MySelfView extends View {
	public MySelfView(Context context) {
        this(context,null);
    }

    public MySelfView(Context context, @Nullable AttributeSet attrs) {
        this(context,attrs,0);
    }

    @SuppressLint("ResourceAsColor")
    public MySelfView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

2.在arrts.xml中设计需要自定义的属性,name要和类名一样的。

  <declare-styleable name="MySelfView">
  		内环颜色
        <attr name="circle_in_bg" format="color"/>
        外环颜色
        <attr name="circle_out_bg" format="color"/>
        环宽
        <attr name="circle_width" format="integer"/>
        当前进度数值
        <attr name="circle_num" format="integer"/>
        内环图标
        <attr name="circle_icon" format="reference"/>
    </declare-styleable>

在xml视图中引用

  <com.ui.view.MySelfView
            android:id="@+id/sv_humidity"
            android:layout_width="72dp"
            android:layout_height="72dp"
            circleview:circle_icon="@mipmap/icon_battery10"
            circleview:circle_in_bg="#3C6366"
            circleview:circle_num="30"
            circleview:circle_out_bg="#4FB8F5"
            circleview:circle_width="8" />

3.在构造函数中引用定义刚刚自定义的属性,并设置默认值

 private int width = 0;
 private int height = 0;
 private int defaultsize = 100;
 private int mArcCenterX;
 private int mCircleInBg = getResources().getColor(R.color.colorAccent1);
 private int mCircleOutBg = getResources().getColor(R.color.colorAccent5);
 private int mCircleWidth = 20;//圆弧的宽度
 private int mCircleNum=0;//显示百分比
 private int mIntervalWith=5;
......
  
public MySelfView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MySelfView);
        mCircleInBg = a.getInteger(R.styleable.MySelfView_circle_in_bg, mCircleInBg);
        mCircleOutBg = a.getInteger(R.styleable.MySelfView_circle_out_bg, mCircleOutBg);
        mCircleWidth = a.getInteger(R.styleable.MySelfView_circle_width, mCircleWidth);
        mCircleNum=a.getInteger(R.styleable.MySelfView_circle_num,mCircleNum);
        a.recycle();
  }

4.定义各个画笔属性,画笔的初始化也在构造函数中,避免多次重复创建画笔占用过多内存

 private Paint mTextPaint;//中间文字
    private Paint mImgPaint;//中间图标
    private Paint inPaint;//底部圆弧画笔
    private Paint outPaint;//外部圆弧画笔
    private Paint inSelPaint;//圆弧百分比显示画笔
   
......
public MySelfView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
  /**
         * Paint.Style.FILL:填充内部
         * Paint.Style.FILL_AND_STROKE  :填充内部和描边
         * Paint.Style.STROKE  :描边
         */
        inPaint.setColor(mCircleInBg);
        inPaint.setStrokeWidth(mCircleWidth);
        inPaint.setStyle(Paint.Style.STROKE);
        //设置抗锯齿,如果不设置,加载位图的时候可能会出现锯齿状的边界,如果设置,边界就会变的稍微有点模糊,锯齿就看不到了。
        inPaint.setAntiAlias(true);//去锯齿
        inPaint.setStrokeCap(Paint.Cap.ROUND); // 设置转弯处为圆角
        outPaint=new Paint();
        outPaint.setColor(getResources().getColor(R.color.white));
        outPaint.setStrokeWidth(mIntervalWith);
        outPaint.setStyle(Paint.Style.FILL);
        //设置抗锯齿,如果不设置,加载位图的时候可能会出现锯齿状的边界,如果设置,边界就会变的稍微有点模糊,锯齿就看不到了。
        outPaint.setAntiAlias(true);//去锯齿

        inSelPaint = new Paint();
        inSelPaint.setColor(getResources().getColor(R.color.colorAccent2));
        inSelPaint.setStrokeWidth(mCircleWidth);
        inSelPaint.setStyle(Paint.Style.STROKE);
        //设置抗锯齿,如果不设置,加载位图的时候可能会出现锯齿状的边界,如果设置,边界就会变的稍微有点模糊,锯齿就看不到了。
        inSelPaint.setAntiAlias(true);//去锯齿
      //  inSelPaint.setStrokeCap(Paint.Cap.ROUND); // 设置转弯处为圆角

        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mImgPaint=new Paint();
        mImgPaint.setAntiAlias(true);
 }

5.在onMeasure方法中定义图形宽高,因为要绘制的是圆形,是宽高相等的,所以计算宽高,获取短的为圆环直径

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getSize(widthMeasureSpec);
        height = getSize(heightMeasureSpec);
        if (width < height) {
            height = width;
        } else {
            width = height;
        }
        setMeasuredDimension(width, height);
      }
    private int getSize(int measureSpec) {
        int mySize = defaultsize;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        switch (mode) {
            case MeasureSpec.UNSPECIFIED: {//如果没有指定大小:就设置为默认大小
                mySize = defaultsize;
                break;
            }
            case MeasureSpec.AT_MOST: {//如果测里模式是最大取值为size
//我们将大小取最大值,你也可以取其他值
                mySize = size;
                break;
            }
            case MeasureSpec.EXACTLY: {//如果是固定的大小,那就不要去改变它
                mySize = size;
                break;
            }
        }
        return mySize;
    }
      @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mArcCenterX = (int) (w / 2.f);
    }

6.在onDraw中进行绘制
先绘制图标,根据图标的位置来计算绘制文字的位置,然后绘制底部圆环,外部圆环
canvas中的rotate方法是绘制圆环的起始角度(起始位置)。
注意:RectF是从左上角开始算起,绘制位置的时候需要考虑到图标和文字的宽度高度
这里简单介绍下Canvas绘制:
画文字:
//文本的x轴的开始位置,文本Y轴的结束位置,画笔对象
canvas.drawText(“开始”, 50, 50, p);
//参数2:路径,参数3:距离路径开始位置的偏移量,参数4:距离路径上下的偏移量(可以为负数)
canvas.drawTextOnPath(“123456”, path, 0, -50, p);
画圆:
//圆心X 圆心Y 半径R
canvas.drawCircle(20,20,10, p);
画线
canvas.drawLine(20,20,10, 20, p);
画一个椭圆
RectF oval = new RectF(15,20,50,40);
canvas.drawOval(oval, p);
画弧度
canvas.drawArc(oval,20,180,false, p);
矩形
canvas.drawRect(10,10,20,20, p);
画圆角矩形
RectF oval3 = new RectF(8,26,20,30);
canvas.drawRoundRect(oval3,20,5, p);
画点
canvas.drawPoint(60,390, p);

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mTextPaint.setTextSize(26);
        canvas.drawText("1234",mArcCenterX-mTextPaint.measureText("1234")/2,height/2,mTextPaint);
        //  Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.mipmap.icon_battery10);
        Bitmap bmp= ImageUtil.drawableToBitamp(getResources().getDrawable(R.mipmap.icon_battery10));
        Bitmap scaledBitmap = Bitmap.createScaledBitmap(bmp, 53, 53, true);
        canvas.drawBitmap(scaledBitmap,width/2-scaledBitmap.getWidth()/2,height/2-scaledBitmap.getHeight()/2-mTextPaint.measureText("1234"),mImgPaint);
        bmp.recycle();

        canvas.rotate(135, mArcCenterX, mArcCenterX);
        //绘制大圆环
        int viewRadius = getWidth() / 2;
        int canterRadius = (getWidth() - mCircleWidth) / 2;
        RectF rectF = new RectF(mCircleWidth / 2, mCircleWidth / 2, canterRadius+viewRadius, canterRadius+viewRadius);
        canvas.drawArc(rectF, 0, 360, false, inPaint);//底部弧形
        canvas.drawArc(rectF, 0, (float) (mCircleNum*3.6), false, inSelPaint);//表示进度的弧形
        //绘制刻度线
        canvas.translate(width / 2, height / 2);
        for (int i = 0; i < 10; i++) {//60等分
            canvas.save();//画布保存
            canvas.rotate(360 + i * 36);//绘制图标的旋转
//            int alpha = (int) ((i / 60f * 255 + circleAlpha) % 255);
//            mPaint.setAlpha(alpha);//设置画笔的透明度[0-255],0是完全透明,255是完全不透明
            canvas.translate(width/2-mCircleWidth, 0);//绘图坐标的平移。
            canvas.drawLine(0, 0, mCircleWidth, 0, outPaint);//绘制线.drawLine,drawLines绘制多条线
            canvas.restore();//合并保存后的图层
        }
    }

将drawable转bitmap

 public static Bitmap drawableToBitamp(Drawable drawable)
    {
        //声明将要创建的bitmap
        Bitmap bitmap = null;
        //获取图片宽度
        int width = drawable.getIntrinsicWidth();
        //获取图片高度
        int height = drawable.getIntrinsicHeight();
        //图片位深,PixelFormat.OPAQUE代表没有透明度,RGB_565就是没有透明度的位深,否则就用ARGB_8888。详细见下面图片编码知识。
        Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
        //创建一个空的Bitmap
        bitmap = Bitmap.createBitmap(width,height,config);
        //在bitmap上创建一个画布
        Canvas canvas = new Canvas(bitmap);
        //设置画布的范围
        drawable.setBounds(0, 0, width, height);
        //将drawable绘制在canvas上
        drawable.draw(canvas);
        return bitmap;
    }

7.如果需要动态加载圆弧进度,添加修改进度值的方法,修改后调用invalidate()方法刷新UI

 //设置当前进度
   public void setmCircleNum(int mCircleNum) {
       this.mCircleNum = mCircleNum;
       invalidate();
   }

完结,以上已经为全部代码

补充知识:

  1. canvas.drawArc绘制圆时的坐标方向是顺时针,顺时针为正,逆时针为负;右0,下90,左180,上270
/**
        * 第二个参数:startAngle起始的角度
        * 第三个参数:sweepAngle绘制的弧度
        * userCenter如果为true,则将椭圆的中心包括在圆弧中
        */
canvas.drawArc(rectF, 110, 1, false, paint);//画圆弧

2.RectF rectF = new RectF(left,top,right,bottom)
左上为起点,右下为终点

  • 参数一:left–矩形左侧的X坐标,矩形区域左上角的横坐标
  • 参数二:top–矩形顶部的Y坐标 ,矩形区域左上角的纵坐标
  • 参数三:right–矩形右侧的X坐标,矩形区域右下角的横坐标
  • 参数四:bottom–矩形底部的Y坐标,矩形区域右下角的纵坐标
    3.设置渐变色画笔
//渐变色颜色组
    private int[] colors1 = new int[]{
           Color.parseColor("#9ce3d5"),
           Color.parseColor("#84cecf"),
           Color.parseColor("#65b6c7")
   };
       @Override
   protected void onDraw(Canvas canvas) {
       super.onDraw(canvas);
......
//渐变色角度设置
		int cY = getHeight() / 2;
       int cX = cY;
       if (getHeight() > getWidth()) {
           cX = getWidth() / 2;
           cY = cX;
       }

       LinearGradient gradientRight = new LinearGradient(cX - radius, cY - radius, // 渐变区域,左上右下
               cX + radius, cY + radius, colors1, null, Shader.TileMode.REPEAT);
       rightCirclePaint.setShader(gradientRight);
       canvas.drawArc(rectF, 10, 100, false, paint);//画圆弧
	}   

在这里插入图片描述
绘制多圆弧进度代码:

public class ThreePercentageView extends View {
   //左边圆弧画笔
   private Paint leftCirclePaint;
   //右边圆弧画笔
   private Paint rightCirclePaint;
   //中间画笔
   private Paint centerCirclePaint;
   private Paint leftPaint, rightPaint, centerPaint;
   //圆弧宽度
   private int circleWidth = 30;
   private int indexCircle = 10;//当前进度
   private int width = 0;
   private int height = 0;
   private int topIndex = 50, leftIndex = 50, rightIndex = 50;
   /**
    * 外层圆弧需要
    */
   private int mArcCenterX;

   private int[] colors1 = new int[]{
           Color.parseColor("#9ce3d5"),
           Color.parseColor("#84cecf"),
           Color.parseColor("#65b6c7")
   };
   private int[] colors2 = new int[]{
           Color.parseColor("#40598c"),
           Color.parseColor("#348ba9"),
           Color.parseColor("#32b0b4")
   };
   private int[] colors3 = new int[]{
           Color.parseColor("#5a09fb"),
           Color.parseColor("#7215f4"),
           Color.parseColor("#9527ea")
   };

   public ThreePercentageView(Context context) {
       this(context, null);
   }

   public ThreePercentageView(Context context, @Nullable AttributeSet attrs) {
       this(context, attrs, 0);
   }

   public ThreePercentageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
       super(context, attrs, defStyleAttr);
       TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PercentageView);
       circleWidth = a.getInteger(R.styleable.PercentageView_circle_width, circleWidth);
       indexCircle = a.getInteger(R.styleable.PercentageView_index_circle, indexCircle);
       /**
        * Paint.Style.FILL:填充内部
        * Paint.Style.FILL_AND_STROKE  :填充内部和描边
        * Paint.Style.STROKE  :描边
        */
       leftCirclePaint = new Paint();
       leftCirclePaint.setStrokeWidth(circleWidth);
       leftCirclePaint.setStyle(Paint.Style.STROKE);
       //设置抗锯齿,如果不设置,加载位图的时候可能会出现锯齿状的边界,如果设置,边界就会变的稍微有点模糊,锯齿就看不到了。
       leftCirclePaint.setAntiAlias(true);//去锯齿
       leftCirclePaint.setStrokeCap(Paint.Cap.ROUND); // 设置转弯处为圆角

       rightCirclePaint = new Paint();
       rightCirclePaint.setStrokeWidth(circleWidth);
       rightCirclePaint.setStyle(Paint.Style.STROKE);
       //设置抗锯齿,如果不设置,加载位图的时候可能会出现锯齿状的边界,如果设置,边界就会变的稍微有点模糊,锯齿就看不到了。
       rightCirclePaint.setAntiAlias(true);//去锯齿
       rightCirclePaint.setStrokeCap(Paint.Cap.ROUND); // 设置转弯处为圆角

       centerCirclePaint = new Paint();
       centerCirclePaint.setStrokeWidth(circleWidth);
       centerCirclePaint.setStyle(Paint.Style.STROKE);
       //设置抗锯齿,如果不设置,加载位图的时候可能会出现锯齿状的边界,如果设置,边界就会变的稍微有点模糊,锯齿就看不到了。
       centerCirclePaint.setAntiAlias(true);//去锯齿
       centerCirclePaint.setStrokeCap(Paint.Cap.ROUND); // 设置转弯处为圆角


       leftPaint = new Paint();
       leftPaint.setStrokeWidth(circleWidth);
       leftPaint.setStyle(Paint.Style.STROKE);
       leftPaint.setColor(getResources().getColor(R.color.white));
       leftPaint.setAntiAlias(true);
       leftPaint.setStrokeCap(Paint.Cap.ROUND);
       rightPaint = new Paint();
       rightPaint.setStrokeWidth(circleWidth);
       rightPaint.setStyle(Paint.Style.STROKE);
       rightPaint.setColor(getResources().getColor(R.color.white));
       rightPaint.setAntiAlias(true);
       rightPaint.setStrokeCap(Paint.Cap.ROUND);
       centerPaint = new Paint();
       centerPaint.setStrokeWidth(circleWidth);
       centerPaint.setStyle(Paint.Style.STROKE);
       centerPaint.setColor(getResources().getColor(R.color.white));
       centerPaint.setAntiAlias(true);
       centerPaint.setStrokeCap(Paint.Cap.ROUND);
   }

   @Override
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       super.onMeasure(widthMeasureSpec, heightMeasureSpec);
       width = getSize(widthMeasureSpec);
       height = getSize(heightMeasureSpec);
       if (width < height) {
           height = width;
       } else {
           width = height;
       }
       setMeasuredDimension(width, height);
   }

   private int getSize(int measureSpec) {
       int mySize = 100;
       int mode = MeasureSpec.getMode(measureSpec);
       int size = MeasureSpec.getSize(measureSpec);
       switch (mode) {
           case MeasureSpec.UNSPECIFIED: {//如果没有指定大小:就设置为默认大小
               mySize = 100;
               break;
           }
           case MeasureSpec.AT_MOST: //如果测里模式是最大取值为size,我们将大小取最大值,你也可以取其他值
           case MeasureSpec.EXACTLY: {//如果是固定的大小,那就不要去改变它
               mySize = size;
               break;
           }
       }
       return mySize;
   }

   @Override
   protected void onSizeChanged(int w, int h, int oldw, int oldh) {
       if (w < h) {
           mArcCenterX = (int) (w / 2.f);
       } else {
           mArcCenterX = (int) (h / 2.f);
       }
       super.onSizeChanged(w, h, oldw, oldh);
   }

   @Override
   protected void onDraw(Canvas canvas) {
       super.onDraw(canvas);

       int viewRadius = getHeight() / 2;
       int canterRadius = (getHeight() - circleWidth) / 2;
       RectF rectF = new RectF(circleWidth / 2, circleWidth / 2, canterRadius + viewRadius, canterRadius + viewRadius);
       float radius = getHeight() / 2;    //圆半径
       int cY = getHeight() / 2;
       int cX = cY;
       if (getHeight() > getWidth()) {
           cX = getWidth() / 2;
           cY = cX;
       }
       canvas.rotate(-30, mArcCenterX, mArcCenterX);
       LinearGradient gradientRight = new LinearGradient(cX - radius, cY - radius, // 渐变区域,左上右下
               cX + radius, cY + radius, colors1, null, Shader.TileMode.REPEAT);
       rightCirclePaint.setShader(gradientRight);
       canvas.drawArc(rectF, 10, 100, false, rightCirclePaint);//画圆弧

       LinearGradient gradient2 = new LinearGradient(cX - radius, cY - radius, // 渐变区域,左上右下
               cX + radius, cY + radius, colors2, null, Shader.TileMode.REPEAT);
       leftCirclePaint.setShader(gradient2);
       canvas.drawArc(rectF, 130, 100, false, leftCirclePaint);//画圆弧


       LinearGradient gradient3 = new LinearGradient(cX - radius, cY - radius, // 渐变区域,左上右下
               cX + radius, cY + radius, colors3, null, Shader.TileMode.REPEAT);
       centerCirclePaint.setShader(gradient3);
       canvas.drawArc(rectF, 250, 100, false, centerCirclePaint);//画圆弧

       /**
        * 画的方向为顺时针,顺时针为正,逆时针为负,右0,下90,左180,上270
        * startAngle起始的角度
        * sweepAngle绘制的弧度
        * userCenter如果为true,则将椭圆的中心包括在圆弧中
        */
       //左边起始角度130
       //top起始角度250
       //右边起始角度10

       // 最大angle是100度,根据当前数据来计算需要的角度,
       // 左边:弧度angle=100*num/max,绘制的偏移角度
       canvas.drawArc(rectF, 110 - rightIndex, 1, false, rightPaint);//画圆弧
       canvas.drawArc(rectF, 130 + leftIndex, 1, false, leftPaint);//画圆弧
       canvas.drawArc(rectF, 250 + topIndex, 1, false, centerPaint);//画圆弧
   }

   //绘制的偏移角度
   public void setAngle(int top, int left, int right) {
       this.leftIndex = left;
       this.rightIndex = right;
       this.topIndex = top;
       invalidate();
   }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值