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

news2024/10/3 4:38:08

效果图:

网上垂直的水波纹进度条很多,但横向的很少,将垂直的水波纹改为水平的还遇到了些麻烦,现在完善后发布出来,希望遇到的人少躺点坑。

思路分析

整体效果可分为三个,绘制圆角背景和圆角矩形,绘制第一条和第二条水波浪,根据自定义进度变化效果。

功能实现

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右边距的右边 所以是* 2
    for (int i = 0; i < waveNumber * 2; i++) {
        wavePath.rQuadTo(waveHeight, waveLength / 2, 0, waveLength);
        wavePath.rQuadTo(-waveHeight, waveLength / 2, 0, waveLength);
    }
    //连接p1 - p2
    wavePath.lineTo(0, waveActualSizeHeight);
    //连接p2 - p0
    wavePath.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;
    //绘制波浪Path
    private 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;
    //bitmap
    private 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,recycle
        typedArray.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));
        }

        //占比一开始设置为0
        currentPercent = currentProgress * 1f / maxProgress;
    }


    @Override
    protected 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右边距的右边 所以是* 2
        for (int i = 0; i < waveNumber * 2; i++) {
            wavePath.rQuadTo(waveHeight, waveLength / 2, 0, waveLength);
            wavePath.rQuadTo(-waveHeight, waveLength / 2, 0, waveLength);
        }
        //连接p1 - p2
        wavePath.lineTo(waveActualSizeWidth, waveActualSizeHeight);
        //连接p2 - p3
        wavePath.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 - p0
        for (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 - p2
        secondWavePath.lineTo(0, waveActualSizeHeight);
        secondWavePath.lineTo(waveActualSizeHeight, waveActualSizeWidth);
        //连接p2 - p1
        secondWavePath.lineTo(waveActualSizeWidth, waveActualSizeHeight + moveDistance);
        //封闭起来填充
        secondWavePath.close();
        return secondWavePath;
    }

    @Override
    protected 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_content
        if (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时表明动画结束
        @Override
        protected 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() {
        @Override
        public 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();
        }
    }
}

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

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

相关文章

阅读HAL源码之重点总结

HAL封装中有如下特点&#xff08;自己总结的&#xff09;&#xff1a; 特定外设要设置的参数组成一个结构体&#xff1b; 特定外设所有寄存器组成一个结构体&#xff1b; 地址基本都是通过宏来定义的&#xff0c;定义了各外设的起始地址&#xff0c;也就是对应寄存器结构体的地…

问答系统(QA)调研

引言 智能问答系统广泛用于回答人们以自然语言形式提出的问题&#xff0c;经典应用场景包括&#xff1a;智能语音交互、在线客服、知识获取、情感类聊天等。根据QA任务&#xff0c;可以将QA大致分为5大类&#xff0c;分别为&#xff1a; 文本问答&#xff08;text-based QA&am…

使用Chemistry Development Kit (CDK) 来进行化学SMILES子结构匹配

摘要 SMILES是一种用于描述化合物结构的字符串表示法&#xff0c;其中子结构搜索是在大规模化合物数据库中查找特定的结构。然而&#xff0c;这种搜索方法存在一个误解&#xff0c;即将化合物的子结构视为一个独立的实体进行搜索&#xff0c;而忽略了它们在更大的化合物中的上…

码匠 × OpenAI :快速生成 SQL 语句,提升开发效率!

目录 使用 OpenAI 生成 SQL 码匠连接与集成 OpenAI 总结 关于码匠 在码匠中&#xff0c;编写 SQL 语句&#xff0c;并结合码匠一系列开箱即用的组件实现复杂的业务逻辑&#xff0c;是很常见的应用开发场景。然而&#xff0c;不同的数据库在 SQL 增删改查操作语法、类型字段和…

【C++进阶】一些小知识点

const限定符 用const给字面常量起个名字&#xff08;标识符&#xff09;&#xff0c;这个标识符就称为标识符常量&#xff1b;因为标识符常量的声明和使用形式很像变量&#xff0c;所以也称常变量。声明方式&#xff1a; const int a 77; const float PI 3.14159f&#xff…

单核CPU, 1G内存,也能做JVM调优吗?

最近&#xff0c;笔者的技术群里有人问了一个有趣的技术话题&#xff1a;单核CPU, 1G内存的超低配机器&#xff0c;怎么做JVM调优&#xff1f;这实际上是两个问题。单核CPU的超低配机器&#xff0c;怎么充分利用CPU&#xff1f;单核CPU, 1G内存的超低配机器&#xff0c;怎么做J…

python学习之OpenCV-Python模块的部分应用示例(生成素描图和动漫图)

文章目录前言一、图片转灰度二、对图片进行二值化处理三、对图片去除噪点四、调整图片透明度五、生成素描滤镜效果图&#xff08;方法结合应用&#xff09;六、生成动漫卡通滤镜效果图&#xff08;方法结合应用&#xff09;总结前言 OpenCV 是一个图像和视频处理库&#xff0c…

由Geoscene Enterprise 2.1 升级至Geoscene Enterprise 3.1

文章目录一、升级前工作二、升级Geoscene portal三、升级Web Adaptor&#xff08;针对portal门户&#xff09;四、升级Server 站点&#xff08;作为门户托管服务器&#xff09;五、升级Web Adaptor&#xff08;针对server&#xff09;六、升级Data Store需求&#xff1a;由GeoS…

bestSync外网转内网操作

一&#xff0e;外网笔记本操作 设置管理员密码打开笔记本电脑&#xff0c;设置管理员密码&#xff08;如果已经设置了的&#xff0c;请忽略该操作&#xff09;:左下角鼠标左键点击->控制面板->用户帐户和家庭安全->用户帐户->密码&#xff0c;密码设置完成后回到桌…

【Acwing 周赛复盘】第90场周赛复盘(2023.2.11)

【Acwing 周赛复盘】第90场周赛复盘&#xff08;2023.2.11&#xff09; 周赛复盘 ✍️ 本周个人排名&#xff1a;1488/2884 AC情况&#xff1a;1/3 这是博主参加的第五次周赛&#xff0c;这次做题的时候&#xff0c;感觉题目好难 &#x1f602; 但是一听y总讲解&#xff0c;又…

ORA error集锦

1、oralce 数据客户端需要安装的问题 保存信息为&#xff1a; “无法连接到数据库&#xff0c;因为数据库客户端软件无法加载。确保已正确安装并配置数据库客户端软件” 从百度网盘下载&#xff0c;并安装win32 oracle client 安装包 2、ORA错误 “执行异常,ORA-00911: inval…

TCP四次挥手

TCP 四次挥手过程是怎样的&#xff1f; TCP 断开连接是通过四次挥手方式。 双方都可以主动断开连接&#xff0c;断开连接后主机中的「资源」将被释放&#xff0c;四次挥手的过程如下图&#xff1a; 客户端打算关闭连接&#xff0c;此时会发送一个 TCP 首部 FIN 标志位被置为 1…

Revit标注问题:尺寸界线长度和“快速尺寸定位标注”

一、 Revit中关于标注的问题 1.有时候&#xff0c;但我们建完模型要进行一定的标注&#xff0c;往往会出现这样的间题&#xff0c;如图1所示 按照正常的标注来说&#xff0c;通常它的标注的正确方式应该是这样的&#xff0c;尺寸界线长度应该是指向图元的&#xff0c;如图2所示…

【源码系列】Faster RCNN源码详解(一)——transform

系列文章目录 文章目录系列文章目录前言一、transform二、总结1.标准化2.缩放3.batch前言 Faster RCNN的源码整体可以分为7个模块&#xff0c;每个模块负责不同的功能。推荐B站up霹雳吧啦Wz讲解的Faster RCNN源码&#xff0c;已经很详细了&#xff0c;这里只是个人的一些理解总…

【Unity VR开发】结合VRTK4.0:创建滑块

语录&#xff1a; 只有经历地狱般的磨练&#xff0c;才能炼出创造天堂的力量。 前言&#xff1a; 滑块是一个非常简单的控件&#xff0c;它允许通过沿有限的驱动轴滑动 Interactable 来选择不同的值。我们将使用线性驱动器创建一个滑块控件&#xff0c;该控件允许我们根据与滑…

蓝桥杯刷题五

1.01背包问题这题就是01背包问题的模板题 回顾一下01背包 01就是这个东西选和不选01背包的表达式是f[i]max(f[i-v]w,f[i]);那么这题就可以直接做了 值得注意的是这里只用了一维数组 所以更新的时候要从后往前面更新#include <bits/stdc.h> using namespace std; const in…

【JDK8新特性之Stream流-Stream结果收集案例实操】

一.JDK8新特性之Stream流-Stream结果收集以及案例实操 二.Stream结果收集(collect函数)-实例实操 2.1 结果收集到集合中 /*** Stream将结果收集到集合中以及具体的实现 collect*/Testpublic void test01(){// 收集到List中 接口List<Integer> list Stream.of(1, 2, 3…

码住!新手容易上手的5个tiktok数据分析网站

当下短视频已经称霸了各大内容平台&#xff0c;越来越多的创作者进入到短视频赛道&#xff0c;为了更好地运营自己的内容平台&#xff0c;数据分析是必不可少的。很多人都入局了tiktok&#xff0c;对于商家或者博主红人来说&#xff0c;这是比较新平台&#xff0c;希望能在这个…

Spring Cloud Gateway的使用

Spring Cloud Gateway网关Spring Cloud Gateway三大核心概念Route(路由)Predicate(断言)Filter(过滤)开始使用动态路由配置路由断言过滤器实现TokenIP验证拦截Spring Cloud Gateway 网关&#xff1a;微服务中最边缘的服务&#xff0c;用来做用户和微服务的桥梁 没有网关❓&…

Python使用VTK对容积超声图像进行体绘制(三维重建)

目录VTK简介什么是体绘制&#xff1f;体绘制效果图流程CodeQ&AReferenceVTK简介 VTK&#xff08;Visualization Toolkit&#xff09;是一个用于3D计算机图形学、图像处理和可视化的开源软件包。它包括一组C类和工具&#xff0c;可以让用户创建和处理复杂的3D图形和数据可视…