Android 自定义View之圆形进度条

news2024/12/25 23:52:57

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

今天我也来写一个。

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

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

那我们要做的就是:

  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 加了背景颜色,方便看出所在位置。

<LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/purple_200">
            <com.test.luodemo.customerview.CircleProgressBar
                android: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);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //绘制底层圆形
        canvas.drawCircle(300, 300, 200, paintCircleBottom);
    }
}

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

3.绘制上层弧形

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

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

@Override
    protected 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则不显示小数点后两位。

	@Override
    protected 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 插值器,动画速度开始慢、逐渐加速。

	@Override
    protected 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() {
            @Override
            public 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;//默认半径
    
	@Override
    protected 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 绘制逻辑 ,

	@Override
    protected 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);


        //绘制上层弧形,从顶部开始,顺时针走 _angle
        canvas.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);
    }

    @Override
    protected 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);
        }
    }

    @Override
    protected 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() {
            @Override
            public 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">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/purple_200">

            <com.test.luodemo.customerview.CircleProgressBar
                android:id="@+id/circle_progress_bar1"
                android:layout_width="300dp"
                android:layout_height="300dp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/teal_200">

            <com.test.luodemo.customerview.CircleProgressBar
                android:id="@+id/circle_progress_bar2"
                android:layout_width="300dp"
                android:layout_height="200dp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/teal_700">

            <com.test.luodemo.customerview.CircleProgressBar
                android:id="@+id/circle_progress_bar3"
                android:layout_width="200dp"
                android:layout_height="300dp" />
        </LinearLayout>

        <!--<LinearLayout
            android:layout_width="50dp"
            android:layout_height="70dp"
            android:background="@color/purple_200">
            <com.test.luodemo.customerview.CircleProgressBar
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/purple_200">
            <com.test.luodemo.customerview.CircleProgressBar
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>-->
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:visibility="visible">

        <Button
            android:id="@+id/button_cpb1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onCPBButtonClick"
            android:text="Button1" />

        <Button
            android:id="@+id/button_cpb2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onCPBButtonClick"
            android:text="Button2" />

        <Button
            android:id="@+id/button_cpb3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onCPBButtonClick"
            android:text="Button3" />

        <Button
            android: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;

    @Override
    protected 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.coloradmin.cn/o/988827.html

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

相关文章

【0908练习】shell脚本使用expr截取网址

题目&#xff1a; 终端输入网址&#xff0c;如&#xff1a;www.hqyj.com&#xff0c; 要求&#xff1a;截取网址每个部分&#xff0c;并放入数组中&#xff0c;不能使用cut&#xff0c;使用expr解决 #!/bin/bash read -p "请输入一个网址" net lenexpr length $net …

协程 VS 线程,Kotlin技术精讲

协程(coroutines)是一种并发设计模式&#xff0c;您可以在Android 平台上使用它来简化异步执行的代码。协程是在版本 1.3 中添加到 Kotlin 的&#xff0c;它基于来自其他语言的既定概念。 在 Android 上&#xff0c;协程有助于管理长时间运行的任务&#xff0c;如果管理不当&a…

无脑014——linux系统,制作coco(json)格式数据集,使用mmdetection训练自己的数据集

电脑&#xff0c;linux&#xff0c;RTX 3090 cuda 11.2 1.制作coco&#xff08;json&#xff09;格式数据集 这里我们使用的标注软件是&#xff1a;labelimg 选择voc格式进行标注&#xff0c;标注之后使用以下代码&#xff0c;把voc格式转换成coco格式&#xff0c;注意最后的路…

机房运维管理软件不知道用哪个好?

云顷网络还原系统V7.0是一款专业的机房运维管理产品&#xff0c;基于局域网络环境&#xff0c;针对中高端机房中电脑运维管理需求所设计开发的。网络还原系统软件通过全面的规划和设计&#xff0c;遵从机房部署、使用到维护阶段化使用方式&#xff0c;通过极速网络同传/增量对拷…

TypeScript的函数

ts与js函数区别 tsjs传参需要规定类型无类型箭头函数箭头函数ES6函数类型无函数类型必填和可选参数所有参数都是可选的能设置默认参数能设置默认参数剩余参数剩余参数 函数重载 函数重载 注释 TypeScript 允许您指定函数的输入和输出值的类型。 输入值注释 // 传参必须为字…

如何理解图神经网络的傅里叶变换和图卷积

图神经网络&#xff08;GNN&#xff09;代表了一类强大的深度神经网络架构。在一个日益互联的世界里&#xff0c;因为信息的联通性&#xff0c;大部分的信息可以被建模为图。例如&#xff0c;化合物中的原子是节点&#xff0c;它们之间的键是边。图神经网络的美妙之处在于它们能…

【设计模式】二、UML 类图概述

文章目录 常见含义含义依赖关系&#xff08;Dependence&#xff09;泛化关系&#xff08;Generalization&#xff09;实现关系&#xff08;Implementation&#xff09;关联关系&#xff08;Association&#xff09;聚合关系&#xff08;Aggregation&#xff09;组合关系&#x…

【赠书活动】AI 时代,程序员无需焦虑

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

9.8day59

503. 下一个更大元素 II - 力扣&#xff08;LeetCode&#xff09; 知识点&#xff1a;单调栈 42. 接雨水 - 力扣&#xff08;LeetCode&#xff09;

初学python爬虫学习笔记——爬取网页中小说标题

初学python爬虫学习笔记——爬取网页中小说标题 一、要爬取的网站小说如下图 二、打开网页的“检查”&#xff0c;查看html页面 发现每个标题是列表下的一个个超链接&#xff0c;从183.html到869.html 可以使用for循环依次得到&#xff1a; x range(183,600) for i in x:pr…

NoSQL数据库入门

一、NoSQL数据库概述 NoSQL 是一种不同于关系数据库的数据库管理系统设计方式&#xff0c;是对非关系型数据库的统称&#xff0c;它所采用的数据模型并非传统关系数据库的关系模型&#xff0c;而是类似键/值、列族、文档等非关系模型。NoSQL 数据库没有固定的表结构&#xff0c…

W25Q16_Flash手册总结

文章目录 前言一、概述&特点1、概述W25Q16BV1、特点2、引脚说明3、内部结构示意图4、操作指令5、操作示例时序图1、写入启用指令&#xff1a;Write Enable&#xff08;06h&#xff09;2、读取状态寄存器指令&#xff1a;Read Status Register-1&#xff08;05h&#xff09;…

【PowerShell代码】清除掉文件中的非英文字母

如果你尝试从网上下载一些带有非ASCII的文件时候&#xff0c;你在这台机器上会发现没有问题&#xff0c;但是将文件传递到其他的地方或者其他电脑你会发现存在比较大的问题&#xff0c;我如何才能将这些文件中的非英文字母去掉呢&#xff1f; 如何才能将文件中的这些非英文字母…

无涯教程-JavaScript - IMLOG2函数

描述 IMLOG2函数以x yi或x yj文本格式返回复数的以2为底的对数。可以从自然对数计算复数的以2为底的对数,如下所示- $$\log_2(x yi)(log_2e)\ln(x yi)$$ 语法 IMLOG2 (inumber)争论 Argument描述Required/OptionalInumberA complex number for which you want the bas…

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

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

Blender中的高级边缘控制和纹理映射

推荐&#xff1a;使用 NSDT场景编辑器 快速搭建3D应用场景 步骤 1 首先&#xff0c;您需要创建一组无阴影材质&#xff0c;每种材质具有不同的颜色&#xff0c;确保您有足够的材质来覆盖模型&#xff0c;而不会有相同的颜色相互重叠。然后&#xff0c;切换到“着色”&#xff…

即拼七人拼团系统开发模式是怎么盈利赚钱的?

即拼七人拼团是市场上最近比较火爆的一款商业模式&#xff0c;它结合了二二复制和拼团两种模式玩法&#xff0c;不仅能让消费者从中获利&#xff0c;还能让平台快速获流裂变&#xff0c;对平台起盘初期和发展中期具有很强的推广能力。那么这个模式是怎么盈利赚钱的呢&#xff1…

使用内网负载机(Linux)执行Jmeter性能测试

一、背景 ​ 在我们工作中有时候会需要使用客户提供的内网负载机进行性能测试&#xff0c;一般在什么情况下我们需要要求客户提供内网负载机进行性能测试呢&#xff1f; 遇到公网环境下性能测试达到了带宽瓶颈。那么这时&#xff0c;我们就需要考虑在内网环境负载机下来执行我们…

Mac brew -v 报错 fatal: detected dubious ownership in repository

Mac 电脑查询 brew版本时报错&#xff0c;如下错误&#xff1a; Last login: Fri Sep 8 14:56:21 on ttys021 sunshiyusunshiyudeMacBook-Pro-2 ~ % brew -v Homebrew 4.0.3-30-g7ac31f7 fatal: detected dubious ownership in repository at /usr/local/Homebrew/Library/Ta…

《人生苦短,我学Python》——列表(List)

昨天&#xff0c;我们学习了一种数据结构——元组。今天我们将学习另一种数据结构——列表。 列表又叫List&#xff0c;与元组的结构类似&#xff0c;也可以用于存储多个类型的数据。接下来&#xff0c;我们一起学习列表的用法–> 文章目录 一、要点先知&#xff1a;二、基…