Android自定义View实现横向的双水波纹进度条
效果图:

网上垂直的水波纹进度条很多,但横向的很少,将垂直的水波纹改为水平的还遇到了些麻烦,现在完善后发布出来,希望遇到的人少躺点坑。
思路分析
整体效果可分为三个,绘制圆角背景和圆角矩形,绘制第一条和第二条水波浪,根据自定义进度变化效果。
功能实现
1、绘制圆角背景和圆角矩形边框
圆角矩形边框:
private RectF rectBorder;
if (rectBorder == null) {rectBorder = new RectF(0.5f * dp1, 0.5f * dp1, waveActualSizeWidth - 0.5f * dp1, waveActualSizeHeight - 0.5f * dp1);
}
canvas.drawRoundRect(rectBorder, dp27, dp27, borderPaint);
我们创建一个新的画布,然后在画布里画上圆角矩形背景和第一条和第二条水波浪:
//这里用到了缓存 根据参数创建新位图
if (circleBitmap == null) {circleBitmap = Bitmap.createBitmap(waveActualSizeWidth, waveActualSizeHeight, Bitmap.Config.ARGB_8888);
}
//以该bitmap为底创建一块画布
if (bitmapCanvas == null) {bitmapCanvas = new Canvas(circleBitmap);
}
// 圆角矩形背景,为了能让波浪填充完整个圆形背景
if (rectBg == null) {rectBg = new RectF(0, 0, waveActualSizeWidth, waveActualSizeHeight);
}
bitmapCanvas.drawRoundRect(rectBg, dp27, dp27, backgroundPaint);
//裁剪图片
canvas.drawBitmap(circleBitmap, 0, 0, null);
2、通过贝塞尔曲线实现双水波
1)实现第一条水波
/*** 绘制波浪线*/
private Path canvasWavePath() {//要先清掉路线wavePath.reset();//起始点移至(0,0) p0 -p1 的高度随着进度的变化而变化wavePath.moveTo((currentPercent) * waveActualSizeWidth, -moveDistance);//最多能绘制多少个波浪//其实也可以用 i < getWidth() ;i+=waveLength来判断 这个没那么完美//绘制p0 - p1 绘制波浪线 这里有一段是超出View的,在View右边距的右边 所以是* 2for (int i = 0; i < waveNumber * 2; i++) {wavePath.rQuadTo(waveHeight, waveLength / 2, 0, waveLength);wavePath.rQuadTo(-waveHeight, waveLength / 2, 0, waveLength);}//连接p1 - p2wavePath.lineTo(0, waveActualSizeHeight);//连接p2 - p0wavePath.lineTo(0, 0);//封闭起来填充wavePath.close();return wavePath;
}
moveDistance为水波垂直方向移动的距离。
waveLength为水波长度,一个上弧加一个下弧为一个波长。
path的起始点为(0,0)可根据进度动态改变,然后循环画曲线,长度是有几个波浪就是多长,然后连接到view高度的位置,最后到(0,0),形成一个封闭的区域,这样就实现了一个填充的水波效果。
2)绘制第二条水波,第二条水波和第一条类似,只是起始点变了:
/*** 绘制第二层波浪*/
private Path canvasSecondPath() {secondWavePath.reset();//初始点移动到下方secondWavePath.moveTo((currentPercent) * waveActualSizeWidth, waveActualSizeHeight + moveDistance);for (int i = 0; i < waveNumber * 2; i++) {secondWavePath.rQuadTo(waveHeight, -waveLength / 2, 0, -waveLength);secondWavePath.rQuadTo(-waveHeight, -waveLength / 2, 0, -waveLength);}secondWavePath.lineTo(0, 0);secondWavePath.lineTo(0, waveActualSizeHeight);secondWavePath.close();return secondWavePath;
}
3、设置动画使进度和水波纹变化
/*** 设置进度** @param currentProgress 进度* @param duration 达到进度需要的时间*/
public void setProgress(int currentProgress, long duration, AnimatorListenerAdapter listenerAdapter) {float percent = currentProgress * 1f / maxProgress;this.currentProgress = currentProgress;//从0开始变化currentPercent = 0;moveDistance = 0;mProgressAnimator = ValueAnimator.ofFloat(0, percent);//设置动画时间mProgressAnimator.setDuration(duration);//让动画匀速播放,避免出现波浪平移停顿的现象mProgressAnimator.setInterpolator(new LinearInterpolator());mProgressAnimator.addUpdateListener(listener);mProgressAnimator.addListener(listenerAdapter);mProgressAnimator.start();// 波浪线startWaveAnimal();
}/*** 波浪动画*/
private void startWaveAnimal() {//动画实例化if (waveProgressAnimator == null) {waveProgressAnimator = new WaveProgressAnimal();//设置动画时间waveProgressAnimator.setDuration(2000);//设置循环播放waveProgressAnimator.setRepeatCount(Animation.INFINITE);//让动画匀速播放,避免出现波浪平移停顿的现象waveProgressAnimator.setInterpolator(new LinearInterpolator());//当前视图开启动画this.startAnimation(waveProgressAnimator);}
}
其中波浪动画是通过改变moveDistance的值改变纵坐标达到,进度主要是通过改变百分比currentPercent改变波浪的横坐标达到。
完整源码:
/*** 横向双水波浪进度条** @author jingbin**/
public class HorizontalWaveProgressView extends View {//绘制波浪画笔private Paint wavePaint;//绘制波浪Pathprivate Path wavePath;//波浪的宽度private final float waveLength;//波浪的高度private final float waveHeight;//波浪组的数量 一个波浪是一低一高private int waveNumber;//自定义View的波浪宽高private int waveDefaultWidth;private int waveDefaultHeight;//测量后的View实际宽高private int waveActualSizeWidth;private int waveActualSizeHeight;//当前进度值占总进度值的占比private float currentPercent;//当前进度值private int currentProgress;//进度的最大值private int maxProgress;//动画对象private WaveProgressAnimal waveProgressAnimator;private ValueAnimator mProgressAnimator;private ValueAnimator mEndAnimator;//波浪平移距离private float moveDistance = 0;//圆形背景画笔private Paint backgroundPaint;// 边框private Paint borderPaint;//bitmapprivate Bitmap circleBitmap;//bitmap画布private Canvas bitmapCanvas;//波浪颜色private final int wave_color;//圆形背景进度框颜色private final int backgroundColor;//进度条显示值监听接口private UpdateTextListener updateTextListener;//是否绘制双波浪线private boolean isShowSecondWave;//第二层波浪的颜色private final int secondWaveColor;//边框色private final int borderColor;//第二层波浪的画笔private Paint secondWavePaint;private Path secondWavePath;private int dp1;// 圆角角度private int dp27;public HorizontalWaveProgressView(Context context) {this(context, null);}public HorizontalWaveProgressView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public HorizontalWaveProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);//获取attrs文件下配置属性TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HorizontalWaveProgressView);//获取波浪宽度 第二个参数,如果xml设置这个属性,则会取设置的默认值 也就是说xml没有指定wave_length这个属性,就会取Density.dip2px(context,25)waveLength = typedArray.getDimension(R.styleable.HorizontalWaveProgressView_wave_length, DensityUtil.dip2px(context, 25));//获取波浪高度waveHeight = typedArray.getDimension(R.styleable.HorizontalWaveProgressView_wave_height, DensityUtil.dip2px(context, 5));//获取波浪颜色wave_color = typedArray.getColor(R.styleable.HorizontalWaveProgressView_wave_color, Color.parseColor("#B76EFF"));//圆形背景颜色backgroundColor = typedArray.getColor(R.styleable.HorizontalWaveProgressView_wave_background_color, Color.WHITE);//当前进度currentProgress = typedArray.getInteger(R.styleable.HorizontalWaveProgressView_currentProgress, 0);//最大进度maxProgress = typedArray.getInteger(R.styleable.HorizontalWaveProgressView_maxProgress, 100);//是否显示第二层波浪isShowSecondWave = typedArray.getBoolean(R.styleable.HorizontalWaveProgressView_second_show, false);//第二层波浪的颜色secondWaveColor = typedArray.getColor(R.styleable.HorizontalWaveProgressView_second_color, Color.parseColor("#DEBCFF"));//边框色borderColor = typedArray.getColor(R.styleable.HorizontalWaveProgressView_border_color, Color.parseColor("#DEBCFF"));//记得把TypedArray回收//程序在运行时维护了一个 TypedArray的池,程序调用时,会向该池中请求一个实例,用完之后,调用 recycle() 方法来释放该实例,从而使其可被其他模块复用。//那为什么要使用这种模式呢?答案也很简单,TypedArray的使用场景之一,就是上述的自定义View,会随着 Activity的每一次Create而Create,//因此,需要系统频繁的创建array,对内存和性能是一个不小的开销,如果不使用池模式,每次都让GC来回收,很可能就会造成OutOfMemory。//这就是使用池+单例模式的原因,这也就是为什么官方文档一再的强调:使用完之后一定 recycle,recycle,recycletypedArray.recycle();init(context);}/*** 初始化一些画笔路径配置*/private void init(Context context) {//设置自定义View的宽高waveDefaultWidth = DensityUtil.dip2px(context, 152);waveDefaultHeight = DensityUtil.dip2px(context, 40);dp1 = DensityUtil.dip2px(getContext(), 1);dp27 = DensityUtil.dip2px(getContext(), 27);wavePath = new Path();wavePaint = new Paint();//设置画笔为取交集模式wavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//设置波浪颜色wavePaint.setColor(wave_color);//设置抗锯齿wavePaint.setAntiAlias(true);//矩形背景backgroundPaint = new Paint();backgroundPaint.setColor(backgroundColor);backgroundPaint.setAntiAlias(true);//边框borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);borderPaint.setColor(borderColor);borderPaint.setAntiAlias(true);borderPaint.setStrokeWidth(dp1);borderPaint.setStyle(Paint.Style.STROKE);if (isShowSecondWave) {//是否绘制双波浪线secondWavePath = new Path();//初始化第二层波浪画笔secondWavePaint = new Paint();secondWavePaint.setColor(secondWaveColor);secondWavePaint.setAntiAlias(true);//因为要覆盖在第一层波浪上,且要让半透明生效,所以选SRC_ATOP模式secondWavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));}//占比一开始设置为0currentPercent = currentProgress * 1f / maxProgress;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//这里用到了缓存 根据参数创建新位图circleBitmap = Bitmap.createBitmap(waveActualSizeWidth, waveActualSizeHeight, Bitmap.Config.ARGB_8888);//以该bitmap为底创建一块画布bitmapCanvas = new Canvas(circleBitmap);// 绘制背景,为了能让波浪填充完整个圆形背景RectF rectBg = new RectF(0, 0, waveActualSizeWidth, waveActualSizeHeight);bitmapCanvas.drawRoundRect(rectBg, dp27, dp27, backgroundPaint);if (isShowSecondWave) {//绘制第二层波浪bitmapCanvas.drawPath(canvasSecondPath(), secondWavePaint);}
//绘制波浪形bitmapCanvas.drawPath(canvasWavePath(), wavePaint);//裁剪图片canvas.drawBitmap(circleBitmap, 0, 0, null);// 绘制边框RectF rectBorder = new RectF(0.5f * dp1, 0.5f * dp1, waveActualSizeWidth - 0.5f * dp1, waveActualSizeHeight - 0.5f * dp1);canvas.drawRoundRect(rectBorder, dp27, dp27, borderPaint);}/*** 绘制波浪线*/private Path canvasWavePath() {//要先清掉路线wavePath.reset();//起始点移至(0,0) p0 -p1 的高度随着进度的变化而变化wavePath.moveTo((currentPercent) * waveActualSizeWidth, -moveDistance);
// wavePath.moveTo(-moveDistance,(1-currentPercent) * waveActualSize);//最多能绘制多少个波浪//其实也可以用 i < getWidth() ;i+=waveLength来判断 这个没那么完美//绘制p0 - p1 绘制波浪线 这里有一段是超出View的,在View右边距的右边 所以是* 2for (int i = 0; i < waveNumber * 2; i++) {wavePath.rQuadTo(waveHeight, waveLength / 2, 0, waveLength);wavePath.rQuadTo(-waveHeight, waveLength / 2, 0, waveLength);}//连接p1 - p2wavePath.lineTo(waveActualSizeWidth, waveActualSizeHeight);//连接p2 - p3wavePath.lineTo(0, waveActualSizeHeight);//连接p3 - p0 p3-p0d的高度随着进度变化而变化wavePath.lineTo(0, 0);//封闭起来填充wavePath.close();return wavePath;}/*** 绘制第二层波浪方法*/private Path canvasSecondPath() {float secondWaveHeight = waveHeight;secondWavePath.reset();//移动到右上方,也就是p1点secondWavePath.moveTo((currentPercent) * waveActualSizeWidth, waveActualSizeHeight + moveDistance);//p1 - p0for (int i = 0; i < waveNumber * 2; i++) {secondWavePath.rQuadTo(secondWaveHeight, -waveLength / 2, 0, -waveLength);secondWavePath.rQuadTo(-secondWaveHeight, -waveLength / 2, 0, -waveLength);}//p3-p0的高度随着进度变化而变化secondWavePath.lineTo(0, 0);//连接p3 - p2secondWavePath.lineTo(0, waveActualSizeHeight);secondWavePath.lineTo(waveActualSizeHeight, waveActualSizeWidth);//连接p2 - p1secondWavePath.lineTo(waveActualSizeWidth, waveActualSizeHeight + moveDistance);//封闭起来填充secondWavePath.close();return secondWavePath;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int width = measureSize(waveDefaultWidth, widthMeasureSpec);int height = measureSize(waveDefaultHeight, heightMeasureSpec);//把View改为正方形setMeasuredDimension(width, height);//waveActualSize是实际的宽高waveActualSizeWidth = width;waveActualSizeHeight = height;//Math.ceil(a)返回求不小于a的最小整数// 举个例子:// Math.ceil(125.9)=126.0// Math.ceil(0.4873)=1.0// Math.ceil(-0.65)=-0.0//这里是调整波浪数量 就是View中能容下几个波浪 用到ceil就是一定让View完全能被波浪占满 为循环绘制做准备 分母越小就约精准waveNumber = (int) Math.ceil(Double.parseDouble(String.valueOf(waveActualSizeHeight / waveLength / 2)));}/*** 返回指定的值** @param defaultSize 默认的值* @param measureSpec 模式*/private int measureSize(int defaultSize, int measureSpec) {int result = defaultSize;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);//View.MeasureSpec.EXACTLY:如果是match_parent 或者设置定值就//View.MeasureSpec.AT_MOST:wrap_contentif (specMode == MeasureSpec.EXACTLY) {result = specSize;} else if (specMode == MeasureSpec.AT_MOST) {result = Math.min(result, specSize);}return result;}//新建一个动画类public class WaveProgressAnimal extends Animation {//在绘制动画的过程中会反复的调用applyTransformation函数,// 每次调用参数interpolatedTime值都会变化,该参数从0渐 变为1,当该参数为1时表明动画结束@Overrideprotected void applyTransformation(float interpolatedTime, Transformation t) {super.applyTransformation(interpolatedTime, t);//左边的距离moveDistance = interpolatedTime * waveNumber * waveLength * 2;//重新绘制invalidate();}}/*** 直接结束** @param duration 结束时间*/public void setProgressEnd(long duration, AnimatorListenerAdapter listenerAdapter) {// 如果是100会不满,因为在波动if (currentProgress == maxProgress) {// 到底了就从头开始currentPercent = 0;}mEndAnimator = ValueAnimator.ofFloat(currentPercent, 1.1f);mEndAnimator.setInterpolator(new DecelerateInterpolator());mEndAnimator.setDuration(duration);mEndAnimator.addUpdateListener(listener);mEndAnimator.addListener(listenerAdapter);mEndAnimator.start();// 波浪线startWaveAnimal();}/*** 设置进度** @param currentProgress 进度* @param duration 达到进度需要的时间*/public void setProgress(int currentProgress, long duration, AnimatorListenerAdapter listenerAdapter) {float percent = currentProgress * 1f / maxProgress;this.currentProgress = currentProgress;//从0开始变化currentPercent = 0;moveDistance = 0;mProgressAnimator = ValueAnimator.ofFloat(0, percent);//设置动画时间mProgressAnimator.setDuration(duration);//让动画匀速播放,避免出现波浪平移停顿的现象mProgressAnimator.setInterpolator(new LinearInterpolator());mProgressAnimator.addUpdateListener(listener);mProgressAnimator.addListener(listenerAdapter);mProgressAnimator.start();// 波浪线startWaveAnimal();}/*** 波浪动画*/private void startWaveAnimal() {//动画实例化if (waveProgressAnimator == null) {waveProgressAnimator = new WaveProgressAnimal();//设置动画时间waveProgressAnimator.setDuration(2000);//设置循环播放waveProgressAnimator.setRepeatCount(Animation.INFINITE);//让动画匀速播放,避免出现波浪平移停顿的现象waveProgressAnimator.setInterpolator(new LinearInterpolator());//当前视图开启动画this.startAnimation(waveProgressAnimator);}}/*** 进度的监听*/ValueAnimator.AnimatorUpdateListener listener = new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {// 当前进度百分比,[0,1]currentPercent = (float) animation.getAnimatedValue();//这里直接根据进度值显示if (updateTextListener != null) {updateTextListener.updateText(currentPercent, maxProgress);}}};public interface UpdateTextListener {/*** 提供接口 给外部修改数值样式 等** @param currentPercent 当前进度百分比* @param maxProgress 进度条的最大数值*/void updateText(float currentPercent, float maxProgress);}/*** 设置监听*/public void setUpdateTextListener(UpdateTextListener updateTextListener) {this.updateTextListener = updateTextListener;}/*** 停止动画,销毁对象*/public void stopAnimal() {if (waveProgressAnimator != null) {waveProgressAnimator.cancel();}if (mProgressAnimator != null && mProgressAnimator.isStarted()) {mProgressAnimator.removeAllListeners();mProgressAnimator.cancel();}if (mEndAnimator != null && mEndAnimator.isStarted()) {mEndAnimator.removeAllListeners();mEndAnimator.cancel();}}
}
相关文章:

Android自定义View实现横向的双水波纹进度条
效果图:网上垂直的水波纹进度条很多,但横向的很少,将垂直的水波纹改为水平的还遇到了些麻烦,现在完善后发布出来,希望遇到的人少躺点坑。思路分析整体效果可分为三个,绘制圆角背景和圆角矩形,绘…...

Python 之 Pandas 分组操作详解和缺失数据处理
文章目录一、groupby 分组操作详解1. Groupby 的基本原理2. agg 聚合操作3. transform 转换值4. apply二、pandas 缺失数据处理1. 缺失值类型1.1 np.nan1.2 None1.3 NA 标量2. 缺失值处理2.1 查看缺失值的情形2.2 缺失值的判断2.3 删除缺失值2.4 缺失值填充在开始之前ÿ…...

【人工智能 AI】什么是人工智能? What is Artificial Intelligence
目录 Introduction to Artificial Intelligence人工智能概论 What is Artificial Intelligence? 什么是人工智能?...

17、触发器
文章目录1 触发器概述2 触发器的创建2.1 创建触发器语法2.2 代码举例3 查看、删除触发器3.1 查看触发器3.2 删除触发器4 触发器的优缺点4.1 优点4.2 缺点4.3 注意点尚硅谷MySQL数据库教程-讲师:宋红康 我们缺乏的不是知识,而是学而不厌的态度 在实际开发…...

内核并发消杀器(KCSAN)技术分析
一、KCSAN介绍KCSAN(Kernel Concurrency Sanitizer)是一种动态竞态检测器,它依赖于编译时插装,并使用基于观察点的采样方法来检测竞态,其主要目的是检测数据竞争。KCSAN是一种检测LKMM(Linux内核内存一致性模型)定义的数据竞争(data race)的工…...

蓄水池抽样算法
蓄水池抽样,也称水塘抽样,是随机抽样算法的一种。基本抽样问题有一批数据(假设为一个数组,可以逐个读取),要从中随机抽取一个数字,求抽得的数字下标。常规的抽样方法是,先读取所有的…...

数据结构预算法之买股票最好时机动态规划(可买卖多次)
一.题目二.思路在动规五部曲中,这个区别主要是体现在递推公式上,其他都和上一篇文章思路是一样的。所以我们重点讲一讲递推公式。这里重申一下dp数组的含义:dp[i][0] 表示第i天持有股票所得现金。dp[i][1] 表示第i天不持有股票所得最多现金如…...
华为OD机试真题Java实现【蛇形矩阵】真题+解题思路+代码(20222023)
蛇形矩阵 蛇形矩阵是由1开始的自然数依次排列成的一个矩阵上三角形。 例如,当输入5时,应该输出的三角形为: 1 3 6 10 15 2 5 9 14 4 8 13 7 12 11请注意本题含有多组样例输入。 🔥🔥🔥🔥🔥👉👉👉👉👉👉 华为OD机试(Java)真题目录汇总 输入描述:…...

spring Bean的生命周期 IOC
文章目录 1. 基础知识1.1 什么是 IoC ?2. 扩展方法3. 源码入口1. 基础知识 1.1 什么是 IoC ? IoC,控制反转,想必大家都知道,所谓的控制反转,就是把 new 对象的权利交给容器,所有的对象都被容器控制,这就叫所谓的控制反转。 IoC 很好地体现了面向对象设计法则之一 —…...
详解cors跨域
文章目录同源策略cors基本概念cors跨域方式简单请求 simple request非简单请求- 预检请求CORS兼容情况CORS总结同源策略 在以前的一篇博客中有介绍,同源策略是一种安全机制,为了预防某些恶意的行为,限制浏览器从不同源文档和脚本进行交互的行…...

ARM uboot 源码分析7 - uboot的命令体系
一、uboot 命令体系基础 1、使用 uboot 命令 (1) uboot 启动后进入命令行环境下,在此输入命令按回车结束,uboot 会收取这个命令然后解析,然后执行。 2、uboot 命令体系实现代码在哪里 (1) uboot 命令体系的实现代码在 uboot/common/cmd_xx…...

物理服务器与云服务器备份相同吗?
自从云计算兴起以来,服务器备份已经从两阶段的模拟操作演变为由云服务器备份软件执行的复杂的多个过程。但是支持物理服务器和虚拟服务器之间的备份相同吗?主要区别是什么?我们接下来将详细讨论这个问题。 物理服务器与云服务器备份的区别 如果您不熟悉虚拟服务器…...

【Linux】system V共享内存 | 消息队列 | 信号量
🌠 作者:阿亮joy. 🎆专栏:《学会Linux》 🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根 目录👉system V共…...
FSC的宣传许可 答疑
【FSC的宣传许可 答疑】问:已经采购了认证产品但没有贴FSC标签,是否可以申请宣传许可?答:不可以。要宣传您采用了FSC认证产品的前提条件之一是产品必须是认证且贴有标签的。如果产品没有贴标,则不可申请宣传许可。您的…...

Leetcode力扣秋招刷题路-0100
从0开始的秋招刷题路,记录下所刷每道题的题解,帮助自己回顾总结 100. 相同的树 给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是…...

协作对象死锁及其解决方案
协作对象死锁及其解决方案 1.前言 在遇到转账等的需要保证线程安全的情况时,我们通常会使用加锁的方式来保证线程安全,但如果无法合理的使用锁,很可能导致死锁。或者有时我们使用线程池来进行资源的使用,如调用数据库࿰…...

良许也成为砖家啦~
大家好,我是良许。 没错,良许成为砖家啦,绝不是口嗨,有图有真相! 有人会说,咦,这明明是严宇啊,跟你良许有啥关系? 额。。老读者应该知道良许的来历—— 鄙人真名严宇&a…...
Java中的编程细节
前言: 学习过程中有不少时候遇到一些看似简单,做起来事倍功半的问题。我也想自己是个聪明人,学东西一听就懂,一学就会,马上就能灵活应用。但这种事不能强求,要么自己要看个十遍二十遍最后理清逻辑…...
Yolov8从pytorch到caffe (一) 环境搭建
Yolov8从pytorch到caffe (一) 环境搭建 1. 创建虚拟环境2. 安装pytorch与v8相关库3. 测试安装是否成功4. 测试推理图像在windows上配置YOLOv8的环境,训练自己的数据集并转换到caffemodel1. 创建虚拟环境 利用conda创建虚拟环境 conda create -n yolo python=3.8 -y 并进入ac…...
2023年CDGA考试-第16章-数据管理组织与角色期望(含答案)
2023年CDGA考试-第16章-数据管理组织与角色期望(含答案) 单选题 1.在定义任何新组织或尝试改进现有组织之前了解当前组织的哪些方面非常重要? A.企业文化、运营模式和人员 B.业务战略、技术战略、数据战略 C.工具、方法和流程 D.事业环境因素、组织过程资产,行动路线图 …...

利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...