一个自定义圆环进度View

啥都不说看,先贴一个图看看最终要实现的效果
这里写图片描述

首先,这个是根据项目需求写出来的一个自定义View,很简单,全是画出来的~~~
好吧,对于一个刚出来的应届毕业生,这是个挑战啊,加上之前的工作很紧张,项目进度太赶,然后Android开发只有我一个人,我能不能说我是个挑大梁的人额。。
哈哈~
好了,废话不多说。。现在开始整题:
1.一开始看到原型的时候,这个控件我就在想能不能用View嵌套View去实现,答案是可以的,但是后来试了一下,发现View嵌套View的形式好像很难控制,所以还是决定自己画出来
2.既然要画,那首先要去了解一下自定义View的介绍吧,然后就去看了一下鸿洋大神的自定义View有了一点理解,然后开始着手去写了~
3.一开始要确定这个View有哪些方法需要抛出来,然后做了一下整理
(1)自定义属性,这个必须的嘛,有些属性不需要在代码中写可以在布局中固定就好了,所以我写了这些自定义的属性:

<declare-styleable name="WorkCircleView">
        <attr name="BorderRadiu" format="dimension"/>
        <attr name="RingRadiu" format="dimension"/>
        <attr name="RingWidth" format="dimension"/>
        <attr name="BorderColor" format="color"/>
        <attr name="RingColor" format="color"/>
        <attr name="ScheduleColor" format="color"/>
        <attr name="UpTextSize" format="dimension"/>
        <attr name="ImageWidth" format="dimension"/>
        <attr name="DownBigTextSize" format="dimension"/>
        <attr name="DownTextSize" format="dimension"/>
        <attr name="ImageHeight" format="dimension"/>
        <attr name="Image" format="reference"/>
    </declare-styleable>

这个自定义属性应该还是能看懂的吧,简单说一下 :
declare-styleable 是给自定义控件添加自定义属性用的
然后declare-styleable后面的name就是这个自定义属性的明明,需要在获取自定义属性时根据这个name去调用的,所以这个名字最好可以和自定义控件名称一样,这样方便查看

attr这个就是自定义属性的标签,里面的name就是自定属性的名称,这个name可以在布局中使用控件后使用app:xxxx=”“这样去调用,想想还是挺简单的,最后说一下这个format这个是自定义属性的类型,至于类型常用的可能是dimension尺寸,可以用dp或者sp,就是说可以定义字体和大小的,然后是reference这个是引用资源的,可以获取资源文件的,然后是color这个是引用的颜色值的,其实也还有其他的类型,需要的可以去网上找找哈~

解释了一大堆,接下来还是说说我的实现思路吧,下面多数会贴代码:
一、新建一个Class集成自View,然后实现里面的3个构造函数,因为用到了自定义属性,所以必须要实现3个构建函数,代码如下:

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

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

    public WorkCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WorkCircleView, 0, 0);
        mBorderRadiu = a.getDimensionPixelOffset(R.styleable.WorkCircleView_BorderRadiu, 100);
        mRingRadiu = a.getDimensionPixelOffset(R.styleable.WorkCircleView_RingRadiu, 70);
        mRingWidth = a.getDimensionPixelOffset(R.styleable.WorkCircleView_RingWidth, 10);
        mBorderColor = a.getColor(R.styleable.WorkCircleView_BorderColor, Color.parseColor("#eaf1f9"));
        mRingColor = a.getColor(R.styleable.WorkCircleView_RingColor, Color.GRAY);
        mScheduleColor = a.getColor(R.styleable.WorkCircleView_ScheduleColor, Color.parseColor("#1cad59"));
        mUpTextSize = a.getDimensionPixelOffset(R.styleable.WorkCircleView_UpTextSize,
                DensityUtil.sp2px(context,14));
        mImageWidth = a.getDimensionPixelOffset(R.styleable.WorkCircleView_ImageWidth, 30);
        mDownBigSize = a.getDimensionPixelOffset(R.styleable.WorkCircleView_DownBigTextSize, 40);
        mDownSize = a.getDimensionPixelSize(R.styleable.WorkCircleView_DownTextSize,30);
        mImageHeight = a.getDimensionPixelOffset(R.styleable.WorkCircleView_ImageHeight,30);
        a.recycle();
        init();
    }

前两个构造函数直接引用第三个构造函数就可以了,然后在第三个构造函数中获取自定义属性,还有初始化画笔,画笔还是一开始初始化完比较好。

二、初始化画笔,把画笔先初始化好先,我这里用了挺多画笔的 =- =,不想在下面清除画笔然后重新定义画笔,是不是很懒,O(∩_∩)O哈哈~,好了,代码如下:

private void init() {
        //初始化画笔
        //边框画笔------start
        mBorderPaint = new Paint();
        mBorderPaint.setAntiAlias(true);
        mBorderPaint.setStyle(Paint.Style.STROKE);
        mBorderPaint.setColor(mBorderColor);
        //-------------end
        //圆环画笔------start
        mRingPaint = new Paint();
        mRingPaint.setAntiAlias(true);
        mRingPaint.setStyle(Paint.Style.STROKE);
        mRingPaint.setColor(mRingColor);
        //-------------end
        //进度画笔------start
        mSchedulePaint = new Paint();
        mSchedulePaint.setAntiAlias(true);
        mSchedulePaint.setStyle(Paint.Style.STROKE);
        //-------------end
        //初始化文字画笔---start
        mUpTextPaint = new Paint();
        mUpTextPaint.setAntiAlias(true);
        mUpTextPaint.setTextSize(mUpTextSize);
        mUpTextPaint.setTypeface(Typeface.DEFAULT_BOLD);//粗体
        mDownBigPaint = new Paint();
        mDownBigPaint.setAntiAlias(true);
        mDownBigPaint.setTextSize(mDownBigSize);
        mDownBigPaint.setTypeface(Typeface.DEFAULT_BOLD);
        mDownPaint = new Paint();
        mDownPaint.setAntiAlias(true);
        mDownPaint.setTextSize(mDownSize);
        //----------------end

        //初始化RectF
        mRectf = new RectF();
    }

三、好了,画笔也初始化完了,现在该考虑的是控件的测量问题了,这个控件不能太小,一小了就会变形,而且什么都没有看到的,所以我们给它一个最小的大小,圆形嘛,宽高一致,在自定义属性那里已经写好了的,所以我们只需要测量就好了:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            width = mBorderRadiu * 2 + getPaddingRight() + getPaddingLeft() + 2;

        }
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = mBorderRadiu * 2 + getPaddingTop() + getPaddingBottom()+ 2;
        }
        int chang = Math.min(width, height);
        mWidth = chang;
        mBorderRadiu = chang / 2;
        mRingRadiu = (int) (mBorderRadiu * 0.87);
        setMeasuredDimension(chang, chang);
    }

这里测量了最大的宽度,然后宽度和高度也是一样的。

四、测量完了,确保了控件能正常显示了,我们要做得就是开始利用Paint(画笔)了,此外,我们要用画笔需要重写onDraw这个方法,onDraw的参数有canvas,这个canvas是什么东东,这个很好解释,有了画笔,我们还需要什么东西?一是画布,二是画家,知道这两个东西了,画布其实就是这个控件本身,而画家嘛,当然是canvas了,canvas里面提供了很多的画法,有画圆弧,圆,矩形,直线,文字。。等等,这个画家还可以嘛,哈哈~,好了,代码贴一下:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int center = mWidth / 2;
        mBorderRadiu = mBorderRadiu - getPaddingLeft() - getPaddingRight();
        if (mBorderRadiu < mRingRadiu || mBorderRadiu - mRingRadiu <= 20) {
            mRingRadiu = (int) (mBorderRadiu * 0.87);
        }
        if (mRingWidth * 3 >= mBorderRadiu) {
            mRingWidth = mBorderRadiu / 4;
        }
        /**
         * 调整圆环的宽度
         */
        mRingPaint.setStrokeWidth(mRingWidth);
        mSchedulePaint.setStrokeWidth(mRingWidth);//跟圆环宽度同宽
        /**
         * 调整进度条颜色
         */
        mSchedulePaint.setColor(mScheduleColor);
        /**
         * 调整文字的颜色
         */
        mUpTextPaint.setColor(Color.BLACK);
        mDownBigPaint.setColor(mScheduleColor);
        mDownPaint.setColor(mScheduleColor);
        //圆环的间隔
        mInterval = mBorderRadiu - mRingRadiu ;
        canvas.drawCircle(center, center, mBorderRadiu, mBorderPaint);
        canvas.drawCircle(center, center, mRingRadiu, mRingPaint);
        //进度的位置
        mRectf.left = mInterval;
        mRectf.top = mInterval;
        mRectf.right = mWidth - mInterval;
        mRectf.bottom = mWidth - mInterval;
        canvas.drawArc(mRectf, -90, -mProgress, false, mSchedulePaint);
        //当图片的宽度小于圆环的半径则画出来
        if (mImageWidth < mBorderRadiu) {
            drawImage(canvas,center);//画图片
            drawUpText(canvas,center);//画上层文字
            drawDownText(canvas,center);//画下层文字
        }
    }

我相信我的注释应该很清晰明了,真正开始画我是另外写了方法的,这样可读性好一点,而且容易找到错误点,所以,每个方法都需要传一个canvas(画家)进去,然后加上算好的位置穿进去,这样就可以画出一个自定义控件了,我觉得还是贴一下代码吧:

//画图片
    private void drawImage(Canvas canvas,int center){
        int imgWidth = Math.min(mImageWidth,mImageHeight);
        if (imgWidth >= mBorderRadiu / 2){
            imgWidth = (int)(mBorderRadiu / 2);
        }
        //画图片
        int imgLeft = center - imgWidth - (mBorderRadiu / 5);
        int imgTop = center - imgWidth;
        int imgRight = center - (mBorderRadiu / 5);
        int imgBottom = center;
        mDrawable.setBounds(imgLeft, imgTop - 5, imgRight, imgBottom - 5);
        mDrawable.draw(canvas);
    }

    //画上层文字
    private void drawUpText(Canvas canvas,int center){
        int textX = center - (mImageWidth / 4);
        int textY = center;
        //画上层大字体,根据图片位置判断字体位置
        canvas.drawText(mUpText, textX, textY - 10 , mUpTextPaint);
    }

    //画下层文字
    private void drawDownText(Canvas canvas,int center){
        //下层字体位置计算
        float DownBigTextWidth = mDownBigPaint.measureText(mDownBigText);
        float downX = center - DownBigTextWidth + (mImageWidth / 5);
        float downY = center + mDownBigSize - (mDownBigSize / 4);
        //画下层大字体
        canvas.drawText(mDownBigText, downX, downY, mDownBigPaint);
        //画下层字体
        float downX2 = center + mImageWidth / 5;
        float downy2 = center + mDownBigSize - (mDownBigSize / 4);
        canvas.drawText(mDownText,downX2 + 5,downy2,mDownPaint);
    }

五、画完了这个之后,就改考虑要抛出的方法了,因为是圆环进度嘛,进度肯定是由用户自己控制的啦,所以,我们把这个进度的控制方法抛出去吧(爸爸不要你了~~):

/**
     * 设置进度
     *
     * @param current 当前值
     * @param total   总值
     */
    public void setProgress(int current, int total) {
        float pro = (float) current / (float) total;
        pro = pro * 360;
        this.mProgress = pro;
        postInvalidate();
    }

    /**
     * 调整颜色进度框
     *
     * @param current  当前值
     * @param total    总值
     * @param colorRes 颜色值
     */
    public void setProgress(int current, int total, int colorRes) {
        float pro = (float) current / (float) total;
        pro = pro * 360;
        this.mProgress = pro;
        this.mScheduleColor = colorRes;
        postInvalidate();
    }

六、到了这里,这个控件就完成了,我们只需要在布局中用起来就好了,还是挺简单的,加班加到奔溃,还是学到挺多的,写个博客激励一下自己,嘿嘿~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要在Android中创建自定义圆环,可以使用Canvas和Paint类来绘制。以下是一个简单的例子: 1. 在你的XML布局文件中,添加一个自定义View: ```xml <com.example.myapp.MyCircleView android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 2. 创建一个自定义View类,并覆盖onDraw方法: ```java public class MyCircleView extends View { private Paint paint; private RectF rectF; private float strokeWidth = 20; //圆环宽度 private float progress = 0; //进度 public MyCircleView(Context context, AttributeSet attrs) { super(context, attrs); paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(strokeWidth); paint.setColor(Color.BLUE); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); rectF = new RectF(strokeWidth / 2, strokeWidth / 2, getWidth() - strokeWidth / 2, getHeight() - strokeWidth / 2); canvas.drawArc(rectF, -90, progress, false, paint); } //设置圆环进度 public void setProgress(float progress) { this.progress = progress; invalidate(); } } ``` 3. 在Activity或Fragment中使用自定义View: ```java MyCircleView myCircleView = findViewById(R.id.my_circle_view); myCircleView.setProgress(120); ``` 在此示例中,我们使用了paint对象来绘制圆环。我们还使用RectF类来确定圆环的大小和位置。最后,我们在onDraw方法中使用canvas对象来绘制圆环。我们还添加了setProgress方法,用于设置进度。 希望这可以帮助到你! ### 回答2: Android中可以通过自定义继承自View的类来实现圆环的绘制。首先,在自定义View类的构造方法中初始化画笔,并设置画笔的属性,如颜色、宽度等。然后,在自定义View类的onDraw()方法中调用canvas的drawCircle()方法,传入圆心坐标和半径参数,即可绘制出一个圆。 此外,要实现圆环的效果,可以在drawCircle()方法之前先绘制一个实心圆,再绘制一个较大的同心空心圆。可以通过设置画笔的样式为STROKE,即只画圆边缘的方式,来实现空心圆的效果。 在绘制圆环的过程中,可以利用onMeasure()方法来获取View的宽高,并动态计算圆心坐标和半径,以适应不同的屏幕尺寸。 另外,如果需要显示进度效果,可以通过设置画笔的样式为FILL,并利用drawArc()方法在圆环内部绘制一个扇形,根据进度值设置绘制的角度,来显示进度条。 最后,在使用自定义圆环的时候,可以在XML布局文件中引用该自定义View,并设置相应的属性,如颜色、宽度、进度值等。 ### 回答3: 在Android中,可以通过自定义控件来实现圆环的效果。 首先,我们可以创建一个自定义View类,继承自View类。在自定义类中,我们需要重写onDraw()方法来绘制圆环。 在onDraw()方法中,我们可以通过Canvas类提供的drawArc()方法来绘制圆弧,从而实现圆环的效果。drawArc()方法需要指定圆弧的矩形区域、起始角度、扫过的角度和是否包含中心点。 为了实现一个圆形的圆环效果,我们可以通过计算得出圆弧的矩形区域,起始角度设置为0,扫过的角度设置为360,表示一个完整的圆环。我们还可以设置画笔的宽度、颜色等属性来控制圆环的样式。 在自定义View类中,我们还可以通过重写onMeasure()方法来控制View的大小。在该方法中,我们可以根据需求设置View的宽度和高度,使得圆环显示出来。 最后,在使用自定义圆环的布局文件中,我们可以直接将自定义View类添加到布局中。可以通过设置布局文件中的宽度、高度,以及其他属性设置来调整圆环的样式和位置。 总结起来,Android中可以通过自定义View类和重写onDraw()方法来实现圆环效果。通过计算矩形区域、设置起始角度和扫过的角度,以及设置画笔的属性,可以实现不同样式的圆环效果。最后,在布局中使用该自定义View类来显示圆环
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值