android——自定义控件(不停变化的textview、开关switch、动画效果的打勾)

news2025/1/11 1:49:51

一、从开始数字到结束数字,不断变化

import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.animation.AccelerateDecelerateInterpolator;

import androidx.appcompat.widget.AppCompatTextView;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;

/**
 * FileName: NumberAnimTextView
 * Date: 2020/12/14
 * Description: TextView动画,从开始到结束,数字不断变化
 * History:
 * <author> <time> <version> <desc>
 */

public class NumberAnimTextView extends AppCompatTextView {

    private String mNumStart = "0";  //
    private String mNumEnd; //

    private long mDuration = 1000;          // 动画持续时间 ms,默认1s

    private String mPrefixString = "";      // 前缀
    private String mPostfixString = "";     // 后缀

    public NumberAnimTextView(Context context) {
        super(context);
    }

    public NumberAnimTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public NumberAnimTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setNumberString(String number) {
        setNumberString("0", number);
    }

    public void setNumberString(String numberStart, String numberEnd) {
        mNumStart = numberStart;
        mNumEnd = numberEnd;
        start();
//        if (checkNumString(numberStart, numberEnd)) {
//            // 数字合法 开始数字动画
//            start();
//        } else {
//            // 数字不合法 直接调用 setText 设置最终值
//            setText(mPrefixString + numberEnd + mPostfixString);
//        }
    }

    public void setDuration(long mDuration) {
        this.mDuration = mDuration;
    }

    public void setPrefixString(String mPrefixString) {
        this.mPrefixString = mPrefixString;
    }

    public void setPostfixString(String mPostfixString) {
        this.mPostfixString = mPostfixString;
    }

    private boolean isInt; // 是否是整数

    /**
     * 校验数字的合法性
     *
     * @param numberStart  开始的数字
     * @param numberEnd    结束的数字
     * @return 合法性
     */
    private boolean checkNumString(String numberStart, String numberEnd) {
        try {
            new BigInteger(numberStart);
            new BigInteger(numberEnd);
            isInt = true;
        } catch (Exception e) {
            isInt = false;
            e.printStackTrace();
        }
        try {
            BigDecimal start = new BigDecimal(numberStart);
            BigDecimal end = new BigDecimal(numberEnd);
            return end.compareTo(start) >= 0;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private void start() {
        ValueAnimator animator = ValueAnimator.ofObject(new BigDecimalEvaluator(), new BigDecimal(mNumStart), new BigDecimal(mNumEnd));
        animator.setDuration(mDuration);
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                BigDecimal value = (BigDecimal) valueAnimator.getAnimatedValue();
                setText(mPrefixString + format(value) + mPostfixString);
            }
        });
        animator.start();
    }

    /**
     * 格式化 BigDecimal ,小数部分时保留两位小数并四舍五入
     *
     * @param bd  BigDecimal
     * @return 格式化后的 String
     */
    private String format(BigDecimal bd) {
        String pattern;
        if (isInt) {
            pattern = "#,###";
        } else {
            pattern = "#,##0.00";
        }
        DecimalFormat df = new DecimalFormat(pattern);
        return df.format(bd);
    }

    class BigDecimalEvaluator implements TypeEvaluator {
        @Override
        public Object evaluate(float fraction, Object startValue, Object endValue) {
            BigDecimal start = (BigDecimal) startValue;
            BigDecimal end = (BigDecimal) endValue;
            BigDecimal result = end.subtract(start);
            return result.multiply(new BigDecimal("" + fraction)).add(start);
        }
    }
}

设置开始数字和结束数字即可:setNumberString("12345", "1234567")

二、自定义开关switch

import android.animation.Animator;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

import com.fslihua.my_application_1.R;


/**
 * 自定义开关Switch
 */
public class CustomSwitch extends View implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener{
    private final String TAG = CustomSwitch.class.getSimpleName();

    //默认的宽高比例
    private static final float DEFAULT_WIDTH_HEIGHT_PERCENT = 0.45f;
    //动画最大的比例
    private static final float ANIMATION_MAX_FRACTION = 1;

    private int mWidth,mHeight;

    //画跑道型背景
    private Paint mBackgroundPain;
    //画背景上的字
    private Paint mDisaboleTextPaint;//开启
    private Paint mEnableTextPaint;//关闭
    //画白色圆点
    private Paint mSlidePaint;

    //是否正在动画
    private boolean isAnimation;

    private ValueAnimator mValueAnimator;

    private float mAnimationFraction;

    private String openText;
    private String closeText;
    private int mOpenColor = Color.GREEN;
    private int mCloseColor = Color.GRAY;
    private int mCurrentColor = Color.GRAY;

    //监听
    private OnCheckedChangeListener mCheckedChangeListener;


    private boolean isChecked;
    public CustomSwitch(Context context) {
        super(context);
        init();
    }

    public CustomSwitch(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomSwitch);
        openText = typedArray.getString(R.styleable.CustomSwitch_openText);
        closeText = typedArray.getString(R.styleable.CustomSwitch_closeText);
        mOpenColor = typedArray.getColor(R.styleable.CustomSwitch_openColor, Color.GREEN);
        mCloseColor = typedArray.getColor(R.styleable.CustomSwitch_closeColor, Color.GRAY);
        mCurrentColor = mCloseColor;
//        mWidth = typedArray.getInteger(R.styleable.CustomSwitch_customWidth,1);
//        mHeight = typedArray.getInteger(R.styleable.CustomSwitch_customHeight,1);
        typedArray.recycle();
        init();
    }

    public CustomSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomSwitch);
        openText = typedArray.getString(R.styleable.CustomSwitch_openText);
        closeText = typedArray.getString(R.styleable.CustomSwitch_closeText);
        mOpenColor = typedArray.getColor(R.styleable.CustomSwitch_openColor, Color.GREEN);
        mCloseColor = typedArray.getColor(R.styleable.CustomSwitch_closeColor, Color.GRAY);
        mCurrentColor = mCloseColor;
//        mWidth = typedArray.getInteger(R.styleable.CustomSwitch_customWidth,1);
//        mHeight = typedArray.getInteger(R.styleable.CustomSwitch_customHeight,1);
        typedArray.recycle();
        init();
    }

    private void init(){
        Log.e(TAG,"init()被调用");
        mBackgroundPain = new Paint();
        mBackgroundPain.setAntiAlias(true);
        mBackgroundPain.setDither(true);
        mBackgroundPain.setColor(Color.GRAY);
//        开启的文字样式
        mDisaboleTextPaint = new Paint();
        mDisaboleTextPaint.setAntiAlias(true);
        mDisaboleTextPaint.setDither(true);
        mDisaboleTextPaint.setStyle(Paint.Style.STROKE);
        mDisaboleTextPaint.setColor(Color.WHITE);
        mDisaboleTextPaint.setTextAlign(Paint.Align.CENTER);
//        关闭的文字样式
        mEnableTextPaint = new Paint();
        mEnableTextPaint.setAntiAlias(true);
        mEnableTextPaint.setDither(true);
        mEnableTextPaint.setStyle(Paint.Style.STROKE);
        mEnableTextPaint.setColor(Color.parseColor("#7A88A0"));
        mEnableTextPaint.setTextAlign(Paint.Align.CENTER);

        mSlidePaint = new Paint();
        mSlidePaint.setColor(Color.WHITE);
        mSlidePaint.setAntiAlias(true);
        mSlidePaint.setDither(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width =  MeasureSpec.getSize(widthMeasureSpec);
        int height = (int) (width*DEFAULT_WIDTH_HEIGHT_PERCENT);
        setMeasuredDimension(width,height);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
    }

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

    private void drawSlide(Canvas canvas){
        float distance = mWidth - mHeight;
//        Log.e(TAG,"distance = " + distance);
//        Log.e(TAG,"mAnimationFraction = " + mAnimationFraction);
//        canvas.drawCircle(mHeight/2+distance*mAnimationFraction,mHeight/2,mHeight/3,mSlidePaint);
        canvas.drawCircle(mHeight/2+distance*mAnimationFraction,mHeight/2, mHeight/2.5f,mSlidePaint);
//        canvas.drawCircle(mHeight/2+distance*mAnimationFraction, (float) (mHeight/2.2), (float) (mHeight/2.2),mSlidePaint);
    }

    private void drawBackground(Canvas canvas){
        Path path = new Path();
        RectF rectF = new RectF(0,0,mHeight,mHeight);
        path.arcTo(rectF,90,180);
        rectF.left = mWidth-mHeight;
        rectF.right = mWidth;
        path.arcTo(rectF,270,180);
        path.close();
        mBackgroundPain.setColor(mCurrentColor);
        canvas.drawPath(path,mBackgroundPain);

//        mDisaboleTextPaint.setTextSize(mHeight/2);
        mDisaboleTextPaint.setTextSize(mHeight/2.2f);
//        mEnableTextPaint.setTextSize(mHeight/2);
        mEnableTextPaint.setTextSize(mHeight/2.2f);
        Paint.FontMetrics fontMetrics = mDisaboleTextPaint.getFontMetrics();
        float top = fontMetrics.top;
        float bottom = fontMetrics.bottom;
//        基线位置
        int baseLine = (int) (mHeight/2 + (bottom-top)*0.3);

        if (!TextUtils.isEmpty(openText)){
            //启用
            mDisaboleTextPaint.setAlpha((int) (255*mAnimationFraction));
            canvas.drawText(openText,mWidth*0.3f,baseLine,mDisaboleTextPaint);
        }

        if (!TextUtils.isEmpty(closeText)){
            //启用
            mEnableTextPaint.setAlpha((int) (255*(1-mAnimationFraction)));
            canvas.drawText(closeText,mWidth*0.7f,baseLine,mEnableTextPaint); //第二个值改变x轴的位置
        }

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                return true;
            case MotionEvent.ACTION_CANCEL:
                break;
            case MotionEvent.ACTION_UP:
                if (isAnimation){
                    return true;
                }
                if (isChecked){
                    startCloseAnimation();
                    isChecked = false;
                    if (mCheckedChangeListener!=null){
                        mCheckedChangeListener.onCheckedChanged(false);
                    }
                }else {
                    startOpeAnimation();
                    isChecked = true;
                    if (mCheckedChangeListener!=null){
                        mCheckedChangeListener.onCheckedChanged(true);
                    }
                }
                return true;


        }
        return super.onTouchEvent(event);
    }

    private void startOpeAnimation(){
        mValueAnimator = ValueAnimator.ofFloat(0.0f, ANIMATION_MAX_FRACTION);
        mValueAnimator.setDuration(500);
        mValueAnimator.addUpdateListener(this);
        mValueAnimator.addListener(this);
        mValueAnimator.start();
        startColorAnimation();
    }
    private void startCloseAnimation(){
        mValueAnimator = ValueAnimator.ofFloat(ANIMATION_MAX_FRACTION, 0.0f);
        mValueAnimator.setDuration(500);
        mValueAnimator.addUpdateListener(this);
        mValueAnimator.addListener(this);
        mValueAnimator.start();
        startColorAnimation();
    }

    private void startColorAnimation(){
        int colorFrom = isChecked?mOpenColor:mCloseColor; //mIsOpen为true则表示要启动关闭的动画
        int colorTo = isChecked? mCloseColor:mOpenColor;
        ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
        colorAnimation.setDuration(500); // milliseconds
        colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animator) {
                mCurrentColor = (int)animator.getAnimatedValue();
            }

        });
        colorAnimation.start();
    }



    //设置监听
    public void setOnCheckedChangeListener(OnCheckedChangeListener listener){
        mCheckedChangeListener = listener;
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
        if (isChecked){
            mCurrentColor = mOpenColor;
            mAnimationFraction = 1.0f;
        }else {
            mCurrentColor = mCloseColor;
            mAnimationFraction = 0.0f;
        }
        invalidate();
    }

    @Override
    public void onAnimationStart(Animator animator) {
        isAnimation = true;
    }

    @Override
    public void onAnimationEnd(Animator animator) {
        isAnimation = false;
    }

    @Override
    public void onAnimationCancel(Animator animator) {
        isAnimation = false;
    }

    @Override
    public void onAnimationRepeat(Animator animator) {
        isAnimation = true;
    }

    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator) {
        mAnimationFraction = (float) valueAnimator.getAnimatedValue();
        invalidate();
    }
    public interface OnCheckedChangeListener{
        void onCheckedChanged(boolean isChecked);
    }
}

attrs.xml代码

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--    开关按钮CustomSwitch的样式定义  -->
    <declare-styleable name="CustomSwitch">
        <attr name="closeText" format="string" />
        <attr name="openText" format="string" />
        <attr name="closeColor" format="color" />
        <attr name="openColor" format="color" />
        <attr name="customWidth" format="integer" />
        <attr name="customHeight" format="integer" />
    </declare-styleable>
</resources>

activity的xml代码:

<com.fslihua.my_application_1.cumstom_ui.CustomSwitch
        android:id="@+id/customSwitch"
        android:layout_marginTop="20dp"
        android:layout_marginStart="20dp"
        android:layout_width="50dp"
        android:layout_height="25dp"
        app:closeColor="#6A6A6A"
        app:closeText="关"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

三、包含动画效果的打勾

import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PathMeasure
import android.util.AttributeSet
import android.util.Log
import android.util.TypedValue
import android.view.View
import android.view.animation.DecelerateInterpolator
import com.fslihua.my_application_1.R

/**
 * 创建时间:2023/11/9
 * 类说明:包含动画效果的打勾
 */
class AnimatedCheckView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
    private val circlePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val checkPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)

    private var circlePath = Path()
    private var checkPath = Path()
    private var circleColor = 0
    private var checkColor = 0
    private var pathLength = 0f
    // 默认值
    private var circleStrokeWidth = 1f
    private var checkStrokeWidth = 1f

    private val animator = ValueAnimator().apply {
        interpolator = DecelerateInterpolator()
        addUpdateListener {
            invalidate()
        }
    }

    private var animateTime: Float = 2f // 2秒

    init {

        context.theme.obtainStyledAttributes(attrs, R.styleable.AnimatedCheckView, 0, 0).apply {
            try {
                circleColor = getColor(R.styleable.AnimatedCheckView_circleColor, Color.BLACK)
                checkColor = getColor(R.styleable.AnimatedCheckView_checkColor, Color.BLACK)
                circleStrokeWidth = getDimension(R.styleable.AnimatedCheckView_circleStrokeWidth, dp2px(context, circleStrokeWidth)
                    .toFloat())
                checkStrokeWidth = getDimension(R.styleable.AnimatedCheckView_checkStrokeWidth, dp2px(context, checkStrokeWidth)
                    .toFloat())
                animateTime = getFloat(R.styleable.AnimatedCheckView_animateTime, animateTime)

                Log.i("AnimatedCheckView", "circleStrokeWidth: $circleStrokeWidth, checkStrokeWidth: $checkStrokeWidth" )
            } finally {
                recycle()
            }
        }

        circlePaint.apply {
            style = Paint.Style.STROKE
            strokeWidth = circleStrokeWidth
            color = circleColor
        }

        checkPaint.apply {
            style = Paint.Style.STROKE
            strokeWidth = checkStrokeWidth
            color = checkColor
        }
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        val centerX = w / 2f
        val centerY = h / 2f
        val radius = w.coerceAtMost(h) / 2f - circleStrokeWidth / 2

        initPaths(centerX, centerY, radius)

        animator.apply {
            setFloatValues(0f, pathLength)
            duration = (animateTime * 1000).toLong()
            start()
        }

        Log.i("AnimatedCheckView", "w: $w, h: $h, centerX: $centerX, centerY: $centerY, radius: $radius" )
    }

    private fun initPaths(centerX: Float, centerY: Float, radius: Float) {
        circlePath.addCircle(centerX, centerY, radius, Path.Direction.CW)

        checkPath.apply {
            moveTo(centerX - radius / 2 - radius / 15, centerY + radius / 15)
            lineTo(centerX - radius / 2, centerY)
            lineTo(centerX - radius / 8, centerY + radius / 3)
            lineTo(centerX + radius / 2, centerY - radius / 3)
        }

        val measure = PathMeasure(circlePath, false)
        val circleLength = measure.length
        measure.setPath(checkPath, false)
        val checkLength = measure.length

        pathLength = circleLength + checkLength
    }


    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        val measure = PathMeasure(circlePath, false)
        val dst = Path()

        val animatedValue = animator.animatedValue as Float
        val circleLength = measure.length
        if (animatedValue < circleLength) {
            measure.getSegment(0f, animatedValue, dst, true)
            canvas.drawPath(dst, circlePaint)
        } else {
            measure.getSegment(0f, circleLength, dst, true)
            canvas.drawPath(dst, circlePaint)

            measure.nextContour()
            measure.setPath(checkPath, false)
            dst.rewind()
            measure.getSegment(0f, animatedValue - circleLength, dst, true)
            canvas.drawPath(dst, checkPaint)
        }
    }

    fun dp2px(context: Context, dpVal: Float): Int {
        return TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            dpVal, context.resources.displayMetrics
        ).toInt()
    }
}

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="AnimatedCheckView">
        <attr name="circleColor" format="color" />
        <attr name="checkColor" format="color" />
        <attr name="circleStrokeWidth" format="dimension" />
        <attr name="checkStrokeWidth" format="dimension" />
        <attr name="animateTime" format="float" />
    </declare-styleable>
</resources>

activity中的代码:

<com.fslihua.my_application_1.cumstom_ui.AnimatedCheckView
        android:id="@+id/ac_success"
        android:layout_width="55dp"
        android:layout_height="55dp"
        android:layout_marginTop="64dp"
        app:animateTime="0.7"
        app:checkColor="#3ACFA5"
        app:checkStrokeWidth="2dp"
        app:circleColor="#3ACFA5"
        app:circleStrokeWidth="2dp"
        app:layout_constraintStart_toEndOf="@+id/customSwitch"
        app:layout_constraintTop_toTopOf="parent" />

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

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

相关文章

OpenCV答题卡识别

文章目录 一、基本流程二、代码实现1.定义函数2.图像预处理&#xff08;1&#xff09;高斯模糊、边缘检测&#xff08;2&#xff09;轮廓检测&#xff08;3&#xff09;透视变换&#xff08;4&#xff09;阈值处理和轮廓检测 3.筛选和排序选项轮廓4.判断答案5.显示结果 三、总结…

ssm基于javaweb的数学竞赛网站的设计与实现+vue

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习&#xff0c;获取源码请私聊我 需要定制请私聊 目 录 目 录 III 第1章 绪论 1 1.1选题动因 1 1.2目的和意义 1 1.3论文结构安排 2 第2章 开发环境与技术 …

Redis 缓存预热,缓存雪崩,缓存击穿,缓存穿透

Spring-data-redis 说明&#xff1a; 在 SpringBoot2.x 之后&#xff0c;原来使用的jedis 被替换为了 lettuce jedis : 采用的直连&#xff0c;多个线程操作的话&#xff0c;是不安全的&#xff0c;如果想要避免不安全的&#xff0c;使用 jedis pool 连接池 lettuce : 采用n…

云计算(第二阶段):mysql后的shell

第一章&#xff1a;变量 前言 什么是shell Shell 是一种提供用户与操作系统内核交互的工具&#xff0c;它接受用户输入的命令&#xff0c;解释后交给操作系统去执行。它不仅可以作为命令解释器&#xff0c;还可以通过脚本完成一系列自动化任务。 shell的特点 跨平台&#xff1a…

【LeetCode】动态规划—124. 二叉树中的最大路径和(附完整Python/C++代码)

动态规划—124. 二叉树中的最大路径和 题目描述前言基本思路1. 问题定义路径的限制&#xff1a; 2. 理解问题和递推关系核心思路&#xff1a;状态定义&#xff1a;递归公式&#xff1a; 3. 解决方法递归 动态规划方法伪代码&#xff1a; 4. 进一步优化5. 小总结 Python代码Pyt…

安装GraphRAG

安装GraphRAG 本文没有安装成功&#xff0c;一直卡在构建图节点。 我用的思路是GraphRAGOllama&#xff08;大语言模型&#xff09;Xinference&#xff08;词嵌入&#xff09;。找到的其他思路是&#xff0c;修改源码。 1 简介 1.1 GraphRAG GraphRAG是微软开源的一种基于…

鸿蒙开发 三十九 ArkTs类 class 静态属性和方法的定义

鸿蒙提供了static关键字&#xff0c;可以用static关键字修饰的属性和方法&#xff0c;用static修饰的属性和方法用类名.的方式调用&#xff0c;如下图&#xff1a; 在Robot方法中定义了静态的属性version&#xff0c;调用的话直接Robot.version就可以了&#xff0c;定义了方法&…

RabbitMQ消息队列MQ脑裂(网络分区)整理分析

文章目录 RabbitMQ 的集群架构基础什么是MQ脑裂检测网络分区RabbitMQ 网络分区导致脑裂的原因• 多个节点认为自己是主节点&#xff1a;• 节点间状态不一致&#xff1a;• 集群的不可用性和错误恢复&#xff1a; RabbitMQ 网络分区引发脑裂的常见场景队列镜像不同步HA&#xf…

人工智能的研究方法

一、人工智能是自然科学和社会科学的交叉学科 1、仿生学&#xff0c;生物构造和功能 2、运筹学&#xff0c;应用数学进行科学决策 3、控制论&#xff0c;通信与控制 4、认知科学&#xff0c;人脑或心智工件机制 5、哲学&#xff0c;世界观和方法论 6、数学&#xff0c;概…

Halcon Blob分析提取小光斑

文章目录 算子complement 返回一个区域的补集select_region_point 选择包含指定像素的所有区域intensity 计算灰度值的均值和偏差 案例 算子 complement 返回一个区域的补集 complement(Region : RegionComplement : : )Region (输入对象)&#xff1a;这指的是输入的一个或多…

[Linux] 软硬链接

软硬连接是两种链接方式,目的是通过使用一个已有的文件,在不进行拷贝的情况下,可以在不同的路径下访问同一份文件 软连接相当于快捷方式 硬连接相当于引用计数 软硬链接的目标也可以是软硬链接 软连接 指令 : ln -s 源文件 链接文件 软连接可链接文件夹 链接文件被删除后文件…

复杂网络基本概念(二)

一、集聚系数 节点i的集聚系数定义&#xff1a; 节点i的k个邻居节点之间实际存在的边数E和总的可能边数之比 所有节点的集聚系数的平均值 二、网络稀疏性与连通性 完全连接网络&#xff1a; 如果一个网络中任意两个节点之间都有来连边存在&#xff0c;则称其是一个完全连接…

探索 Python 装饰器的新境界:wrapt 库的神秘力量

文章目录 探索 Python 装饰器的新境界&#xff1a;wrapt 库的神秘力量背景&#xff1a;为何选择 wrapt&#xff1f;wrapt 是什么&#xff1f;如何安装 wrapt&#xff1f;简单的 wrapt 库函数使用方法创建简单装饰器保持元信息处理参数传递 场景应用&#xff1a;wrapt 的实际用例…

某知名国企面试题

引言 金九银十&#xff0c;求职热潮再度来袭。最近&#xff0c;有位同学去一家知名国企应聘&#xff0c;回来后带回了一套面试题。这套面试题非常典型&#xff0c;其中包含了许多供应链金融方面的典型问题。这些问题很有分享的价值&#xff0c;大家也可以先自己独立思考一下&a…

38 Spring

38 Spring 参考资料 Spring-全面详解&#xff08;学习总结&#xff09; 基本概念 Spring理念 : 使现有技术更加实用 . 本身就是一个大杂烩 , 整合现有的框架技术。 Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器&#xff08;框架&#xff09;。 IOC本质 IOC全…

【Redis】分布式(day12)

引入 在实际生产中&#xff0c;只部署一个Redis时&#xff0c;就会造成单点问题&#xff1a; 可用性问题&#xff0c;单个节点部署的Redis&#xff0c;如果此时该服务器挂了&#xff0c;那么意味着Redis整体的服务也就断掉了。性能/并发也是比较有限的。 为了避免单点问题的…

如何在UE5中创建加载屏幕(开场动画)?

第一步&#xff1a; 首先在虚幻商城安装好Async Loading Screen&#xff0c;并且在项目的插件中勾选好。 第二步&#xff1a; 确保准备好所需要的素材&#xff1a; 1&#xff09;开头的动画视频 2&#xff09;关卡加载图片 3&#xff09;准备至少两个关卡 第三步&#xff1a…

通信工程学习:什么是SPI串行外设接口

SPI&#xff1a;串行外设接口 SPI&#xff0c;即串行外设接口&#xff08;Serial Peripheral Interface&#xff09;&#xff0c;是一种由Motorola公司首先在其MC68HCXX系列处理器上定义的同步串行接口技术。SPI接口主要用于微控制器&#xff08;MCU&#xff09;与外部设备之间…

spring |Spring Security安全框架 —— 认证流程实现

文章目录 开头简介环境搭建入门使用1、认证1、实体类2、Controller层3、Service层3.1、接口3.2、实现类3.3、实现类&#xff1a;UserDetailsServiceImpl 4、Mapper层3、自定义token认证filter 注意事项小结 开头 Spring Security 官方网址&#xff1a;Spring Security官网 开…

leetcode 1027 最长等差数列 题目的思考

https://leetcode.cn/problems/longest-arithmetic-subsequence/ 如果序列是&#xff1a;3 0 3&#xff0c;枚举的公差是3 对于第一个数3&#xff0c;它的序列长度就是他自己3 对于第二个数0&#xff0c;它的序列长度就行它自己0 对于第三个数&#xff0c;它的序列长度应该是【…