Android 自定义View之圆形进度条

很多场景下都用到这种进度条,有的还带动画效果,
在这里插入图片描述

今天我也来写一个。

写之前先拆解下它的组成:

  • 底层圆形
  • 上层弧形
  • 中间文字

那我们要做的就是:

  1. 绘制底层圆形;
  2. 在同位置绘制上层弧形,但颜色不同;
  3. 在中心点绘制文本,显示进度。

按照这个目标,学习下自定义View的流程。

1.基础

新建一个类,继承 View ,重写构造函数,如,

package com.test.luodemo.customerview;import android.content.Context;
import android.util.AttributeSet;
import android.view.View;import androidx.annotation.Nullable;public class CircleProgressBar extends View {public CircleProgressBar(Context context) {super(context);}public CircleProgressBar(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public CircleProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}
}

在 xml 中使用,LinearLayout 加了背景颜色,方便看出所在位置。

<LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/purple_200"><com.test.luodemo.customerview.CircleProgressBarandroid:layout_width="300dp"android:layout_height="300dp"/></LinearLayout>

此时运行,是没效果的,因为这个View还没有绘制,啥也没有。

2.绘制底层圆形

初始化3个图形的画笔 ,底层圆形和上层弧形的画笔宽度一致、颜色不一致,方便区分

重写 onDraw(Canvas canvas) 方法,用 canvas.drawCircle 绘制底层圆形,

package com.test.luodemo.customerview;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;import androidx.annotation.Nullable;public class CircleProgressBar extends View {private Paint paintCircleBottom = new Paint();private Paint paintArcTop = new Paint();private Paint paintText = new Paint();public CircleProgressBar(Context context) {super(context);init();}public CircleProgressBar(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}public CircleProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init(){//初始化文本的画笔paintText.setFlags(Paint.ANTI_ALIAS_FLAG);paintText.setColor(Color.BLACK);paintText.setTextAlign(Paint.Align.CENTER);paintText.setTextSize(80f);//初始化底层圆形的画笔paintCircleBottom.setFlags(Paint.ANTI_ALIAS_FLAG);paintCircleBottom.setColor(Color.LTGRAY);paintCircleBottom.setStrokeWidth(10f);paintCircleBottom.setStrokeCap(Paint.Cap.ROUND);paintCircleBottom.setStyle(Paint.Style.STROKE);//初始化弧形的画笔paintArcTop.setFlags(Paint.ANTI_ALIAS_FLAG);paintArcTop.setColor(Color.MAGENTA);paintArcTop.setStrokeWidth(10f);paintArcTop.setStrokeCap(Paint.Cap.ROUND);paintArcTop.setStyle(Paint.Style.STROKE);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制底层圆形canvas.drawCircle(300, 300, 200, paintCircleBottom);}
}

效果,
在这里插入图片描述

3.绘制上层弧形

在之前的基础上绘制上层弧形,弧形的中心和圆心一致。

用 canvas.drawArc 绘制弧形。这里直接指定绘制的角度是 90° ,后续会动态指定。

@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制底层圆形canvas.drawCircle( 300, 300, 200, paintCircleBottom);//绘制上层弧形,从顶部开始,顺时针走90°_angle = 90;canvas.drawArc(100,100,500,500,270, _angle,false, paintArcTop);}

效果,
在这里插入图片描述

4.绘制文本

用 canvas.drawText 绘制文本,

使用 DecimalFormat 格式化输入,保留小数点后两位,如果小数点后两位都是0则不显示小数点后两位。

	@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制底层圆形canvas.drawCircle(300, 300, 200, paintCircleBottom);//绘制上层弧形,从顶部开始,顺时针走90°_angle = 90;canvas.drawArc(100,100,500,500,270, _angle,false, paintArcTop);//绘制文本DecimalFormat dt = new DecimalFormat("0.##");canvas.drawText(dt.format(100 * _angle/360)+"%", 300 , 300, paintText);}

效果,
在这里插入图片描述

可以看到,文本虽然居中,但是文本是显示在中心线上,
在这里插入图片描述

期望结果是文本的水平中心线和圆心重合,改为,

		//绘制文本,文字中心和圆心保持一致Paint.FontMetrics fontMetrics = paintText.getFontMetrics();float distance =(fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;float baseline= 300 + distance;canvas.drawText(dt.format(100 * _angle/360)+"%", 300, baseline, paintText);//文字中心和圆心一致        

效果,
在这里插入图片描述

5.添加动画

创建一个接口,供外部调用。

使用 ValueAnimator ,监听动画过程,然后逐渐刷新角度值。使用 AccelerateInterpolator 插值器,动画速度开始慢、逐渐加速。

	@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制底层圆形canvas.drawCircle(300, 300, 200, paintCircleBottom);//绘制上层弧形,从顶部开始,顺时针走90°canvas.drawArc(100,100,500,500,270, _angle,false, paintArcTop);//绘制文本,文字中心和圆心保持一致DecimalFormat dt = new DecimalFormat("0.##");Paint.FontMetrics fontMetrics = paintText.getFontMetrics();float distance =(fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;float baseline= 300 + distance;canvas.drawText(dt.format(100 * _angle/360)+"%", 300, baseline, paintText);//文字中心和圆心一致}/*** 设置进度,展现动画* */public void setProgress(int progress){ValueAnimator animator = ValueAnimator.ofFloat(0,100f);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float cur = (float) animation.getAnimatedValue();_angle = cur/100 * 360 * progress/100;invalidate(); //刷新 View}});animator.setDuration(3000);animator.setInterpolator(new AccelerateInterpolator());animator.start();}

注意要去掉 3.绘制上层弧形 中固定90°的逻辑。

外部调用,

CircleProgressBar mCircleProgressBar1 = (CircleProgressBar) findViewById(R.id.circle_progress_bar1);
mCircleProgressBar1.setProgress((int) (100 * Math.random()));

随机生成一个 0.0 - 0.1 的数值,乘以 100 设置为进度。
效果,
在这里插入图片描述
可以看到动画效果, 虽然 git 丢帧了 ~ 。

6.调整位置、宽高

前文我是设定了 View 宽高都是 300dp ,并且绘制图形是随意指定的坐标。

实际开发时,不可能用这些值,所以要优化下绘制的逻辑。

实际使用时,可能宽度高度一样,宽度大于高度 ,宽度小于高度,

采用这个逻辑:

  • 取宽度、高度的最小值,作为圆的直径,除以 2 得到半径。
  • 对角线交汇点作为圆心。

简言之,以对角线为圆心画最大内切圆。

重写 onMeasure 方法,重绘 View 的宽高,这部分参考《Android 开发艺术探索》,

	private int DEFAULT_WIDTH = 100;//默认宽度private int DEFAULT_HEIGHT = 100;//默认宽度private int DEFAULT_RADIUS = 50;//默认半径@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);} else if (widthMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WIDTH, heightMeasureSpec);} else if (heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(widthMeasureSpec, DEFAULT_HEIGHT);}}

修改 onDraw 绘制逻辑 ,

	@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 圆心坐标是(centerX,centerY)int centerX = getWidth()/2;int centerY = getHeight()/2;//确定半径float radius = Math.min(centerX, centerY) - paintCircleBottom.getStrokeWidth();//绘制底层圆形canvas.drawCircle(centerX, centerY, radius, paintCircleBottom);//绘制上层弧形,从顶部开始,顺时针走 _anglecanvas.drawArc(centerX - radius,centerY-radius,centerX + radius,centerY + radius,270, _angle,false, paintArcTop);//绘制文本,文字中心和圆心保持一致Paint.FontMetrics fontMetrics = paintText.getFontMetrics();float distance =(fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;float baseline= centerY + distance;canvas.drawText(dt.format(100 * _angle/360)+"%", centerX, baseline, paintText);//文字中心和圆心一致}

分别写了 3 个布局,布局依次是 宽度等于高度 、宽度大宇高度、宽度小于高度,效果,
在这里插入图片描述
至此,基本是一个还可以的版本了。

附代码

贴下当前代码,
CircleProgressBar.java

package com.test.luodemo.customerview;import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;import androidx.annotation.Nullable;import java.text.DecimalFormat;public class CircleProgressBar extends View {private Paint paintCircleBottom = new Paint();private Paint paintArcTop = new Paint();private Paint paintText = new Paint();private int DEFAULT_WIDTH = 100;//默认宽度private int DEFAULT_HEIGHT = 100;//默认宽度private int DEFAULT_RADIUS = 50;//默认半径private float _angle;//弧形的角度public CircleProgressBar(Context context) {super(context);init();}public CircleProgressBar(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}public CircleProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init(){//初始化文本的画笔paintText.setFlags(Paint.ANTI_ALIAS_FLAG);paintText.setColor(Color.BLACK);paintText.setTextAlign(Paint.Align.CENTER);paintText.setTextSize(80f);//初始化底层圆形的画笔paintCircleBottom.setFlags(Paint.ANTI_ALIAS_FLAG);paintCircleBottom.setColor(Color.LTGRAY);paintCircleBottom.setStrokeWidth(10f);paintCircleBottom.setStrokeCap(Paint.Cap.ROUND);paintCircleBottom.setStyle(Paint.Style.STROKE);//初始化弧形的画笔paintArcTop.setFlags(Paint.ANTI_ALIAS_FLAG);paintArcTop.setColor(Color.MAGENTA);paintArcTop.setStrokeWidth(10f);paintArcTop.setStrokeCap(Paint.Cap.ROUND);paintArcTop.setStyle(Paint.Style.STROKE);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);} else if (widthMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WIDTH, heightMeasureSpec);} else if (heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(widthMeasureSpec, DEFAULT_HEIGHT);}}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 圆心坐标是(centerX,centerY)int centerX = getWidth()/2;int centerY = getHeight()/2;//确定半径float radius = Math.min(centerX, centerY) - paintCircleBottom.getStrokeWidth();//绘制底层圆形canvas.drawCircle(centerX, centerY, radius, paintCircleBottom);//绘制上层弧形,从顶部开始,顺时针走90°canvas.drawArc(centerX - radius,centerY-radius,centerX + radius,centerY + radius,270, _angle,false, paintArcTop);//绘制文本,文字中心和圆心保持一致DecimalFormat dt = new DecimalFormat("0.##");Paint.FontMetrics fontMetrics = paintText.getFontMetrics();float distance =(fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;float baseline= centerY + distance;canvas.drawText(dt.format(100 * _angle/360)+"%", centerX, baseline, paintText);//文字中心和圆心一致}/*** 设置进度,展现动画* */public void setProgress(int progress){ValueAnimator animator = ValueAnimator.ofFloat(0,100f);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float cur = (float) animation.getAnimatedValue();_angle = cur/100 * 360 * progress/100;invalidate();}});animator.setDuration(3000);animator.setInterpolator(new AccelerateInterpolator());animator.start();}
}

布局文件,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".customerview.CircleProgressBarActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/purple_200"><com.test.luodemo.customerview.CircleProgressBarandroid:id="@+id/circle_progress_bar1"android:layout_width="300dp"android:layout_height="300dp" /></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/teal_200"><com.test.luodemo.customerview.CircleProgressBarandroid:id="@+id/circle_progress_bar2"android:layout_width="300dp"android:layout_height="200dp" /></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/teal_700"><com.test.luodemo.customerview.CircleProgressBarandroid:id="@+id/circle_progress_bar3"android:layout_width="200dp"android:layout_height="300dp" /></LinearLayout><!--<LinearLayoutandroid:layout_width="50dp"android:layout_height="70dp"android:background="@color/purple_200"><com.test.luodemo.customerview.CircleProgressBarandroid:layout_width="match_parent"android:layout_height="match_parent"/></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/purple_200"><com.test.luodemo.customerview.CircleProgressBarandroid:layout_width="wrap_content"android:layout_height="wrap_content"/></LinearLayout>--></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:visibility="visible"><Buttonandroid:id="@+id/button_cpb1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onCPBButtonClick"android:text="Button1" /><Buttonandroid:id="@+id/button_cpb2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onCPBButtonClick"android:text="Button2" /><Buttonandroid:id="@+id/button_cpb3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onCPBButtonClick"android:text="Button3" /><Buttonandroid:id="@+id/button_cpb_all"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onCPBButtonClick"android:text="Button All" /></LinearLayout></LinearLayout>

Activity 调用

public class CircleProgressBarActivity extends AppCompatActivity {private CircleProgressBar mCircleProgressBar1 , mCircleProgressBar2 , mCircleProgressBar3;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_circle_progress_bar);Objects.requireNonNull(getSupportActionBar()).setTitle("CircleProgressBarActivity");mCircleProgressBar1 = (CircleProgressBar) findViewById(R.id.circle_progress_bar1);mCircleProgressBar2 = (CircleProgressBar) findViewById(R.id.circle_progress_bar2);mCircleProgressBar3 = (CircleProgressBar) findViewById(R.id.circle_progress_bar3);}public void onCPBButtonClick(View view) {switch (view.getId()) {case R.id.button_cpb1:mCircleProgressBar1.setProgress((int) (100 * Math.random()));break;case R.id.button_cpb2:mCircleProgressBar2.setProgress((int) (100 * Math.random()));break;case R.id.button_cpb3:mCircleProgressBar3.setProgress((int) (100 * Math.random()));break;case R.id.button_cpb_all:mCircleProgressBar1.setProgress((int) (100 * Math.random()));mCircleProgressBar2.setProgress((int) (100 * Math.random()));mCircleProgressBar3.setProgress((int) (100 * Math.random()));break;default:break;}}
}

7.自定义属性 attr

需求是不停的,会有这些需求:可指定画笔(宽度、颜色等)、可指定动画时长等。

这些可以通过创建 Java 接口来设置,但我要学自定义View,就要用 attr

未完待续~~

参考资料:
Android Canvas的drawText()和文字居中方案

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/72503.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2651. 计算列车到站时间

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;数学 知识回忆除法运算 写在最后 Tag 【数学】 题目来源 2651. 计算列车到站时间 题目解读 给你一个列车预计到达时间点和一个列车延误的时间&#xff0c;请返回列车实际的到达时间。 解题思路 方法一&#xff1a;数…

C语言每日一练--Day(16)

本专栏为c语言练习专栏&#xff0c;适合刚刚学完c语言的初学者。本专栏每天会不定时更新&#xff0c;通过每天练习&#xff0c;进一步对c语言的重难点知识进行更深入的学习。 今日练习题关键字&#xff1a;寻找奇数 峰值 二分查找 &#x1f493;博主csdn个人主页&#xff1a;小…

C# 基础面试题(万字)

1.选择题 1. 简述下面选项能够捕获运算溢出的异常类型的有 &#xff1f; A)Exception B)SystemException C)ArithmeticException D)OverflowException 试题回答&#xff1a;AD 2. 程序员可使用&#xff08;&#xff09;语句以程序方式引发异常 &#xff1f; A)run B)try C)th…

jframe生成柱状图片+图片垂直合并+钉钉机器人推送

需求&#xff1a; 后端根据数据自动生成2个图片&#xff0c;然后把两张图片合并成一张图片&#xff0c;再发到钉钉群里&#xff0c;涉及到定时生成和推送&#xff0c;当时我们测试同事说他们写定时脚本放到服务器上&#xff0c;然后让我提供生成图片的方法和钉钉机器人的逻辑 天…

【计算机网络】UDP协议详解

目录 前言 端口号的拓展 端口号范围划分 netstat pidof UDP协议 UDP协议端格式 UDP的特点 面向数据报 UDP的缓冲区 UDP使用注意事项 基于UDP的应用层协议 前言 我们前面讲完了http和https协议&#xff0c;它们都属于应用层&#xff0c;按照TCP/IP五层模…

2023国赛数学建模C题模型代码

C题代码全部都完成了&#xff0c;可以看文末名片 我们先看C题的一个背景 在生鲜商超中,蔬菜类商品保鲜期短,且品相会随销售时间增加而变差。商超需要根据历史销售和需求每天进行补货。由于蔬菜品种众多、产地不同,补货时间在凌晨,商家须在不明确具体单品和价格的情况下进行补…

如何排查网站及APP数据泄露的源头

近年来数据泄露安全事件频发&#xff0c;在今年的hw网络安全攻防演练中&#xff0c;获取敏感信息、数据泄露等漏洞的得分也越来越高&#xff0c;我们SINE安全近十年来成功的帮助了许多客户&#xff0c;查找到了数据泄露的原因&#xff0c;在这里向大家分享我们的经验与心得&…

端口已被占用

报的错误 Exception in thread "Thread-76" java.net.BindException: Address already in use: bindat sun.nio.ch.Net.bind0(Native Method)at sun.nio.ch.Net.bind(Net.java:433)at sun.nio.ch.Net.bind(Net.java:425)at sun.nio.ch.ServerSocketChannelImpl.bind…

实相融、云启未来,智慧公厕让城市生活更美好

现代社会&#xff0c;随着科技的不断发展&#xff0c;人们对于城市生活的要求也在不断提升。在这个过程中&#xff0c;智慧公厕作为城市基础设施中的重要组成部分&#xff0c;正在发挥着越来越重要的作用。通过数字化、云管理、人工智能等未来的科技方式&#xff0c;智慧公厕为…

Acwing算法心得——街灯(差分)

大家好&#xff0c;我是晴天学长&#xff0c;差分广泛用于一段范围的加减运算&#xff0c;可以优化时间复杂度&#xff0c;需要的小伙伴请自取哦&#xff01;如果觉得写的不错的话&#xff0c;可以点个关注哦&#xff0c;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1…

计及电池储能寿命损耗的微电网经济调度(matlab代码)

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序参考文献《考虑寿命损耗的微网电池储能容量优化配置》模型&#xff0c;以购售电成本、燃料成本和储能寿命损耗成本三者之和为目标函数&#xff0c;创新考虑储能寿命损耗约束、放电深度约束和储能循环次…

为什么零基础选择语言首选python

在众多编程语言中&#xff0c;似乎已经没有什么能够阻挡Python的步伐。本月Python又是第一名&#xff0c;市场份额达到了13.42%&#xff0c;在2023年&#xff0c;Python已经连续7个月蝉联榜首&#xff0c;遥遥领先于其他对手。 每个月榜单发布后&#xff0c;都有小伙伴会好奇&…

数学建模竞赛常用代码总结-PythonMatlab

数学建模过程中有许多可复用的基础代码&#xff0c;在此对 python 以及 MATLAB 中常用代码进行简单总结&#xff0c;该总结会进行实时更新。 一、文件读取 python (pandas) 文件后缀名&#xff08;扩展名&#xff09;并不是必须的&#xff0c;其作用主要一方面是提示系统是用…

每日一题(设计循环队列)

每日一题&#xff08;设计循环队列&#xff09; 622. 设计循环队列 - 力扣&#xff08;LeetCode&#xff09; 1.题意解读 本题只能为队列开辟k个单位空间&#xff0c;并且只能利用这几个空间进行数据的存储。 思路&#xff1a;本题使用数组来实现队列是比较方便的&#xff0c…

网络是如何进行通信

网络是如何进行通信的 简介 在现代社会中&#xff0c;网络已经成为我们生活中不可或缺的一部分。从上网搜索信息、在线购物到远程工作和社交媒体&#xff0c;我们几乎无时无刻不与网络保持着联系。但是&#xff0c;网络究竟是个什么玩意&#xff0c;它是如何工作的呢&#xf…

OpenCV 01(图像加载与显示)

一、机器视觉 现在说的机器视觉(Machine Vision)一般指计算机视觉(Computer Vision), 简单来说就是研究如何使机器看懂东西。就是是指用摄影机和电脑代替人眼对目标进行识别、跟踪和测量等机器视觉&#xff0c;并进一步做图形处理&#xff0c;使电脑处理成为更适合人眼观察或传…

道一云·七巧对接打通金蝶云星空查询七巧表单接口与供应商新增接口

道一云七巧对接打通金蝶云星空查询七巧表单接口与供应商新增接口 数据源平台:道一云七巧 道一云七巧拥有强大的自定义表单设计工具&#xff0c;并配置工作流程&#xff0c;通过流程智能流转&#xff0c;打通各个业务场景中的审批、协作环节&#xff0c;包含数据采集单、任务单…

用于独立系统应用的光伏MPPT铅酸电池充电控制器建模(Simulink实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【0906 C高级day1】 Linux中的文件相关指令

一、使用cut截取出Ubuntu用户的家目录&#xff0c;要求&#xff1a;不能使用":"作为分割 grep "ubuntu" /etc/passwd | cut -d "/" -n -f 2-3 | cut -c 1-11 二、思维导图 文件相关指令&#xff1a;

WordPress(5)在主题中添加文章字数和预计阅读时间

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 样式图一、添加位置二、找到主题文件样式图 提示:以下是本篇文章正文内容,下面案例可供参考 一、添加位置 二、找到主题文件 在主题目录下functions.php文件把下面的代码添加进去: // 文章字数…