自定义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. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
【5层】公司办公楼全套设计+++(3156平,含计算书、建筑图,结构图、实习报告,PKPM,答辩PPT) 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值