自定义View没那么可怕~进度圈

本着无图言x的原则,所以先上效果:

这里写图片描述

很简单!也很常见!

所以这样子比较适合入门,因为这个是自定义单个控件,所以只会用到ondraw()、onmeasure()两个方法,所以面试的时候不要傻傻的说自定义view要重写onlayout(),要分情况的!

下面我们一步一步来:

  • 继承view,重写构造方法,至于重写那个构造方法,要看情况咯,CircleView(Context context) 一个参数构造 一般是用于Java代码对象view的创建,CircleView(Context context, AttributeSet attrs) 两个参数构造用于布局声明,但也有1~3构造全部写的,我觉得没必要

  • 自定义view一般都会用到画笔,毕竟图是我们自己实现的,随意我们需要在构造里初始化画笔,当然还有画笔的还需要修饰,比如:颜色、大小、样式等等

  • 接着就是要在画布上画出我们想要的东西了,画布在我们的ondraw()里面,至于里面的逻辑都不是很难,简单的数据计算

  • 当然这时候效果已经出来了,但还不尽人意,比如:你会发现你的布局文件设置宽高为wrap_content和match_parent没有区别,这些问题,我们接下来就会在码代码的时候讲


我先贴下代码,没有onmeasure()哦!

public class CircleView extends View {
    private float sweepAngle;//圆弧经过的角度

    private Paint rPaint;//矩形的画笔
    private Paint progressPaint;//圆弧的画笔
    private Paint textPaint;//进度画笔
    private int precent = 0;//更新百分比
    CircleAnim anim;//内部动画

    public CircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
        anim = new CircleAnim();
    }

    private void init(Context context, AttributeSet attrs) {
        rPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        rPaint.setStyle(Paint.Style.STROKE);//不填充
        rPaint.setColor(Color.GRAY);
        rPaint.setStrokeWidth(15);

        progressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        progressPaint.setStyle(Paint.Style.STROKE);//不填充
        progressPaint.setColor(Color.RED);
        progressPaint.setStrokeWidth(15);

        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setStyle(Paint.Style.STROKE);
        textPaint.setColor(Color.BLACK);
        textPaint.setFakeBoldText(true);//设置粗体
        textPaint.setTextSize(40);
        textPaint.setTextAlign(Paint.Align.CENTER);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int min = Math.min(getWidth(), getHeight());
        int centre = min / 2; // 获取圆心的x坐标
        int radius = centre - 15 / 2;// 半径
        RectF rectF = new RectF(centre - radius, centre - radius, centre + radius, centre + radius);
        canvas.drawArc(rectF, 0, 360, false, rPaint);
        canvas.drawArc(rectF, 0, sweepAngle, false, progressPaint);//这里角度0对应的是三点钟方向,顺时针方向递增
        canvas.drawText(precent + "%", centre, centre, textPaint);
    }



    public class CircleAnim extends Animation {

        public CircleAnim() {
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            sweepAngle = interpolatedTime * 360;
            precent = (int) (interpolatedTime * 100);
            invalidate();
        }
    }


    //设置动画时间
    public void setProgressNum(int time) {
        anim.setDuration(time);
        this.startAnimation(anim);
    }

}

ondraw()方法:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int min = Math.min(getWidth(), getHeight());
        int centre = min / 2; // 获取圆心的x坐标
        int radius = centre - 15 / 2;// 半径
        RectF rectF = new RectF(centre - radius, centre - radius, centre + radius, centre + radius);
        canvas.drawArc(rectF, 0, 360, false, rPaint);
        canvas.drawArc(rectF, 0, sweepAngle, false, progressPaint);//这里角度0对应的是三点钟方向,顺时针方向递增
        canvas.drawText(precent + "%", centre, centre, textPaint);
    }
  • 获取view的宽高,进而获取圆弧的的中心坐标,为什么会取宽高的最小值呢?如:w:150 h:100 看下效果图:

这里写图片描述

下面一部分怎么少了呢? 我们的圆弧半径是取值于宽高的,如果宽度大于高度,而又用宽度来做半径的取值标准,就会导致这样效果,因为宽度是150,也就是半径是75,但高度只有100,那肯定会有一部分绘制不出来的;但如果我们按最小的算就不一样了,怎么算都够,高度100作为取值标准,半径就是50,而宽度是150,所以肯定够你绘制,这就是我们取宽、高最小值的原因。

  • 接着就是画出矩形、圆弧、文本了。

动画

@Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            sweepAngle = interpolatedTime * 360;
            precent = (int) (interpolatedTime * 100);
            invalidate();
        }
  • 主要是这个方法第一个参数的值是0.0~1.0 ;不敢乱翻译。所以你们可以看下源码解释:
/**
     * Helper for getTransformation. Subclasses should implement this to apply
     * their transforms given an interpolation value.  Implementations of this
     * method should always replace the specified Transformation or document
     * they are doing otherwise.
     *
     * @param interpolatedTime The value of the normalized time (0.0 to 1.0)
     *        after it has been run through the interpolation function.
     * @param t The Transformation object to fill in with the current
     *        transforms.
     */
    protected void applyTransformation(float interpolatedTime, Transformation t) {
    }

主要是通过invalidate(); 来刷新界面(注意:invalidate()和postInvalidate()的区别,后者可以在子线程刷新ui),你启动这个动画后,applyTransformation()方法会不断的执行,直到interpolatedTime 变为1.0,同时,invalidate() 会不停的调用ondraw(),这就实现了连贯的更新动画


这样基本没什么问题了,但。。。真的没问题了吗?

我们把代码改成这样:

 <com.example.administrator.kotlinapp.CircleView
       android:id="@+id/circleview_100"
       android:layout_marginTop="50dp"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       />

效果图:

这里写图片描述

纳尼???wrap_content和match_parent效果一样有没有。
所以我们就需要自己测量咯

onmeasure():

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int measurewidth = 400;
        int measureheight = 400;
        measurewidth = resolveSize(measurewidth, widthMeasureSpec);
        measureheight = resolveSize(measureheight, heightMeasureSpec);
        setMeasuredDimension(measurewidth, measureheight);

    }
  • resolveSize()是不是很少见?但你一定经常见这样代码段:
        final int specMode = MeasureSpec.getMode(measureSpec);
        final int specSize = MeasureSpec.getSize(measureSpec);
        final int result;
        switch (specMode) {
            case MeasureSpec.AT_MOST:
                if (specSize < size) {
                    result = specSize | MEASURED_STATE_TOO_SMALL;
                } else {
                    result = size;
                }
                break;
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            case MeasureSpec.UNSPECIFIED:
            default:
                result = size;
        }

没错这就是resolveSize()的源码,以前我们都是自己写这样的逻辑

  1. MeasureSpec.UNSPECIFIED: 不限制,一般我们会给一个默认值
  2. MeasureSpec.EXACTLY:有上限,不能超过限制
  3. MeasureSpec.AT_MOST:限制固定尺寸

ok,各种模式已经适配完了,这样就支持wrap_content、match_parent、固定值了。

最后附上 demo:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的纺织品企业财务管理系统,源码+数据库+毕业论文+视频演示 在如今社会上,关于信息上面的处理,有任何一个企业或者个人会忽视,如何让信息急速传递,并且归档储存查询,采用之前的纸张记录模式已经不符合当前使用要求了。所以,对纺织品企业财务信息管理的提升,也为了对纺织品企业财务信息进行更好的维护,纺织品企业财务管理系统的出现就变得水到渠成不可缺少。通过对纺织品企业财务管理系统的开发,不仅仅可以学以致用,让学到的知识变成成果出现,也强化了知识记忆,扩大了知识储备,是提升自我的一种很好的方法。通过具体的开发,对整个软件开发的过程熟练掌握,不论是前期的设计,还是后续的编码测试,都有了很深刻的认知。 纺织品企业财务管理系统通过MySQL数据库与Spring Boot框架进行开发,纺织品企业财务管理系统能够实现对财务人员,员工,收费信息,支出信息,薪资信息,留言信息,报销信息等信息的管理。 通过纺织品企业财务管理系统对相关信息的处理,让信息处理变的更加的系统,更加的规范,这是一个必然的结果。已经处理好的信息,不管是用来查找,还是分析,在效率上都会成倍的提高,让计算机变得更加符合生产需要,变成人们不可缺少的一种信息处理工具,实现了绿色办公,节省社会资源,为环境保护也做了力所能及的贡献。 关键字:纺织品企业财务管理系统,薪资信息,报销信息;SpringBoot
要实现倒计时圆形进度,你可以创建一个自定义View,使用 Canvas 和 Paint 来绘制圆形和进度条。以下是一个示例代码: ```java public class CountdownCircleView extends View { private int maxProgress = 100; // 总进度 private int progress = 100; // 当前进度 private int circleColor = Color.GRAY; // 圆形颜色 private int progressColor = Color.BLUE; // 进度条颜色 private Paint circlePaint; private Paint progressPaint; public CountdownCircleView(Context context) { super(context); init(); } public CountdownCircleView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public CountdownCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { circlePaint = new Paint(); circlePaint.setAntiAlias(true); circlePaint.setColor(circleColor); progressPaint = new Paint(); progressPaint.setAntiAlias(true); progressPaint.setColor(progressColor); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int viewWidth = getWidth(); int viewHeight = getHeight(); // 绘制圆形 int diameter = Math.min(viewWidth, viewHeight); int radius = diameter / 2; int centerX = viewWidth / 2; int centerY = viewHeight / 2; canvas.drawCircle(centerX, centerY, radius, circlePaint); // 绘制进度条 RectF rectF = new RectF(centerX - radius, centerY - radius, centerX + radius, centerY + radius); float sweepAngle = 360f * progress / maxProgress; canvas.drawArc(rectF, -90, sweepAngle, true, progressPaint); } public void setMaxProgress(int maxProgress) { this.maxProgress = maxProgress; } public void setProgress(int progress) { this.progress = progress; invalidate(); // 重新绘制 } } ``` 你可以将上述代码放入你的 Android 项目中,并在布局文件中使用 `CountdownCircleView`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值