Android原生实现控件outline方案(API28及以上)

news2024/11/19 0:28:16

Android控件的Outline效果的实现方式有很多种,这里介绍一下另一种使用Canvas.drawPath()方法来绘制控件轮廓Path路径的实现方案(API28及以上)。

实现效果:
在这里插入图片描述

属性

添加Outline相关属性,主要包括颜色和Stroke宽度:

<declare-styleable name="shape_button">
	...
	
        <attr name="carbon_stroke" />
        <attr name="carbon_strokeWidth" />
</declare-styleable>

StrokeView接口

创建一个StrokeView通用接口:

/**
 * 外部轮廓相关
 */
public interface StrokeView {
    ColorStateList getStroke();

    void setStroke(ColorStateList color);
    void setStroke(int color);

    float getStrokeWidth();

    void setStrokeWidth(float strokeWidth);
}

ShapeButton 实现这个StrokeView接口:

public class ShapeButton extends AppCompatButton
        implements ShadowView,
        ShapeModelView,
        RippleView,
        StrokeView {
    public ShapeButton(@NonNull Context context) {
        super(context);
        initButton(null, android.R.attr.buttonStyle, R.style.carbon_Button);
    }

    public ShapeButton(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initButton(attrs, android.R.attr.buttonStyle, R.style.carbon_Button);
    }

    public ShapeButton(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initButton(attrs, defStyleAttr, R.style.carbon_Button);
    }

    public ShapeButton(Context context, String text, OnClickListener listener) {
        super(context);
        initButton(null, android.R.attr.buttonStyle, R.style.carbon_Button);
        setText(text);
        setOnClickListener(listener);
    }

    private static int[] elevationIds = new int[]{
            R.styleable.shape_button_carbon_elevation,
            R.styleable.shape_button_carbon_elevationShadowColor,
            R.styleable.shape_button_carbon_elevationAmbientShadowColor,
            R.styleable.shape_button_carbon_elevationSpotShadowColor
    };

    private static int[] cornerCutRadiusIds = new int[]{
            R.styleable.shape_button_carbon_cornerRadiusTopStart,
            R.styleable.shape_button_carbon_cornerRadiusTopEnd,
            R.styleable.shape_button_carbon_cornerRadiusBottomStart,
            R.styleable.shape_button_carbon_cornerRadiusBottomEnd,
            R.styleable.shape_button_carbon_cornerRadius,
            R.styleable.shape_button_carbon_cornerCutTopStart,
            R.styleable.shape_button_carbon_cornerCutTopEnd,
            R.styleable.shape_button_carbon_cornerCutBottomStart,
            R.styleable.shape_button_carbon_cornerCutBottomEnd,
            R.styleable.shape_button_carbon_cornerCut
    };

    private static int[] rippleIds = new int[]{
            R.styleable.shape_button_carbon_rippleColor,
            R.styleable.shape_button_carbon_rippleStyle,
            R.styleable.shape_button_carbon_rippleHotspot,
            R.styleable.shape_button_carbon_rippleRadius
    };

    private static int[] strokeIds = new int[]{
            R.styleable.shape_button_carbon_stroke,
            R.styleable.shape_button_carbon_strokeWidth
    };

    protected TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);

    private void initButton(AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.shape_button, defStyleAttr, defStyleRes);
        Carbon.initElevation(this, a, elevationIds);
        Carbon.initCornerCutRadius(this,a,cornerCutRadiusIds);
        Carbon.initRippleDrawable(this,a,rippleIds);
        // 初始化Stroke相关属性
        Carbon.initStroke(this,a,strokeIds);
        a.recycle();
    }



    // -------------------------------
    // shadow
    // -------------------------------


    // -------------------------------
    // shape
    // -------------------------------


    // -------------------------------
    // ripple
    // -------------------------------


    // -------------------------------
    // stroke
    // -------------------------------

    private ColorStateList stroke;
    private float strokeWidth;
    private Paint strokePaint;

	// 绘制轮廓
    private void drawStroke(Canvas canvas) {
        strokePaint.setStrokeWidth(strokeWidth * 2);
        strokePaint.setColor(stroke.getColorForState(getDrawableState(), stroke.getDefaultColor()));
        // cornersMask这是之前装载控件轮廓的path对象
        cornersMask.setFillType(Path.FillType.WINDING);
        canvas.drawPath(cornersMask, strokePaint);
    }

// 设置轮廓颜色
    @Override
    public void setStroke(ColorStateList colorStateList) {
        stroke = colorStateList;

        if (stroke == null)
            return;

        if (strokePaint == null) {
            strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            strokePaint.setStyle(Paint.Style.STROKE);
        }
    }
	// 设置轮廓颜色
    @Override
    public void setStroke(int color) {
        setStroke(ColorStateList.valueOf(color));
    }

    @Override
    public ColorStateList getStroke() {
        return stroke;
    }
	// 设置轮廓线条宽度
    @Override
    public void setStrokeWidth(float strokeWidth) {
        this.strokeWidth = strokeWidth;
    }

    @Override
    public float getStrokeWidth() {
        return strokeWidth;
    }

}

初始化Stroke属性

    public static void initStroke(StrokeView strokeView, TypedArray a, int[] ids) {
        int carbon_stroke = ids[0];
        int carbon_strokeWidth = ids[1];

        View view = (View) strokeView;
        ColorStateList color =  a.getColorStateList(carbon_stroke);

        if (color != null)
            strokeView.setStroke(color);
        strokeView.setStrokeWidth(a.getDimension(carbon_strokeWidth, 0));
    }

绘制轮廓

onDraw()方法的super.draw(canvas);后面执行drawStroke()方法:

public void drawInternal(@NonNull Canvas canvas) {
        super.draw(canvas);
        if(stroke!=null){
            drawStroke(canvas);
        }
    }

如何使用

<com.chinatsp.demo1.shadow.ShapeButton
                android:id="@+id/show_dialog_btn"
                android:layout_width="wrap_content"
                android:layout_height="36dp"
                android:layout_margin="@dimen/carbon_padding"
                android:background="#ffffff"
                android:stateListAnimator="@null"
                android:text="TOM"
                app:carbon_cornerCut="4dp"
                app:carbon_elevation="30dp"
                app:carbon_elevationShadowColor="#40ff0000"
                app:carbon_rippleColor="#40ff0000"
                app:carbon_rippleStyle="borderless"
                app:carbon_rippleRadius="30dp"
                app:carbon_stroke="@color/carbon_green_400"
                app:carbon_strokeWidth="1dp"/>

完整代码:

public class ShapeButton extends AppCompatButton
        implements ShadowView,
        ShapeModelView,
        RippleView,
        StrokeView {
    public ShapeButton(@NonNull Context context) {
        super(context);
        initButton(null, android.R.attr.buttonStyle, R.style.carbon_Button);
    }

    public ShapeButton(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initButton(attrs, android.R.attr.buttonStyle, R.style.carbon_Button);
    }

    public ShapeButton(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initButton(attrs, defStyleAttr, R.style.carbon_Button);
    }

    public ShapeButton(Context context, String text, OnClickListener listener) {
        super(context);
        initButton(null, android.R.attr.buttonStyle, R.style.carbon_Button);
        setText(text);
        setOnClickListener(listener);
    }

    private static int[] elevationIds = new int[]{
            R.styleable.shape_button_carbon_elevation,
            R.styleable.shape_button_carbon_elevationShadowColor,
            R.styleable.shape_button_carbon_elevationAmbientShadowColor,
            R.styleable.shape_button_carbon_elevationSpotShadowColor
    };

    private static int[] cornerCutRadiusIds = new int[]{
            R.styleable.shape_button_carbon_cornerRadiusTopStart,
            R.styleable.shape_button_carbon_cornerRadiusTopEnd,
            R.styleable.shape_button_carbon_cornerRadiusBottomStart,
            R.styleable.shape_button_carbon_cornerRadiusBottomEnd,
            R.styleable.shape_button_carbon_cornerRadius,
            R.styleable.shape_button_carbon_cornerCutTopStart,
            R.styleable.shape_button_carbon_cornerCutTopEnd,
            R.styleable.shape_button_carbon_cornerCutBottomStart,
            R.styleable.shape_button_carbon_cornerCutBottomEnd,
            R.styleable.shape_button_carbon_cornerCut
    };

    private static int[] rippleIds = new int[]{
            R.styleable.shape_button_carbon_rippleColor,
            R.styleable.shape_button_carbon_rippleStyle,
            R.styleable.shape_button_carbon_rippleHotspot,
            R.styleable.shape_button_carbon_rippleRadius
    };

    private static int[] strokeIds = new int[]{
            R.styleable.shape_button_carbon_stroke,
            R.styleable.shape_button_carbon_strokeWidth
    };

    protected TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);

    private void initButton(AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.shape_button, defStyleAttr, defStyleRes);
        Carbon.initElevation(this, a, elevationIds);
        Carbon.initCornerCutRadius(this,a,cornerCutRadiusIds);
        Carbon.initRippleDrawable(this,a,rippleIds);
        Carbon.initStroke(this,a,strokeIds);
        a.recycle();
    }



    // -------------------------------
    // shadow
    // -------------------------------
    private float elevation = 0;
    private float translationZ = 0;
    private ColorStateList ambientShadowColor, spotShadowColor;
    @Override
    public float getElevation() {
        return elevation;
    }

    @Override
    public void setElevation(float elevation) {
        if (Carbon.IS_PIE_OR_HIGHER) {
            super.setElevation(elevation);
            super.setTranslationZ(translationZ);
        } else if (Carbon.IS_LOLLIPOP_OR_HIGHER) {
            if (ambientShadowColor == null || spotShadowColor == null) {
                super.setElevation(elevation);
                super.setTranslationZ(translationZ);
            } else {
                super.setElevation(0);
                super.setTranslationZ(0);
            }
        } else if (elevation != this.elevation && getParent() != null) {
            ((View) getParent()).postInvalidate();
        }
        this.elevation = elevation;
    }

    @Override
    public float getTranslationZ() {
        return translationZ;
    }

    public void setTranslationZ(float translationZ) {
        if (translationZ == this.translationZ)
            return;
        if (Carbon.IS_PIE_OR_HIGHER) {
            super.setTranslationZ(translationZ);
        } else if (Carbon.IS_LOLLIPOP_OR_HIGHER) {
            if (ambientShadowColor == null || spotShadowColor == null) {
                super.setTranslationZ(translationZ);
            } else {
                super.setTranslationZ(0);
            }
        } else if (translationZ != this.translationZ && getParent() != null) {
            ((View) getParent()).postInvalidate();
        }
        this.translationZ = translationZ;
    }

    @Override
    public ColorStateList getElevationShadowColor() {
        return ambientShadowColor;
    }

    @Override
    public void setElevationShadowColor(ColorStateList shadowColor) {
        ambientShadowColor = spotShadowColor = shadowColor;
        setElevation(elevation);
        setTranslationZ(translationZ);
    }

    @Override
    public void setElevationShadowColor(int color) {
        ambientShadowColor = spotShadowColor = ColorStateList.valueOf(color);
        setElevation(elevation);
        setTranslationZ(translationZ);
    }

    @Override
    public void setOutlineAmbientShadowColor(ColorStateList color) {
        ambientShadowColor = color;
        if (Carbon.IS_PIE_OR_HIGHER) {
            super.setOutlineAmbientShadowColor(color.getColorForState(getDrawableState(), color.getDefaultColor()));
        } else {
            setElevation(elevation);
            setTranslationZ(translationZ);
        }
    }

    @Override
    public void setOutlineAmbientShadowColor(int color) {
        setOutlineAmbientShadowColor(ColorStateList.valueOf(color));
    }

    @Override
    public int getOutlineAmbientShadowColor() {
        return ambientShadowColor.getDefaultColor();
    }

    @Override
    public void setOutlineSpotShadowColor(int color) {
        setOutlineSpotShadowColor(ColorStateList.valueOf(color));
    }

    @Override
    public void setOutlineSpotShadowColor(ColorStateList color) {
        spotShadowColor = color;
        if (Carbon.IS_PIE_OR_HIGHER) {
            super.setOutlineSpotShadowColor(color.getColorForState(getDrawableState(), color.getDefaultColor()));
        } else {
            setElevation(elevation);
            setTranslationZ(translationZ);
        }

    }

    @Override
    public int getOutlineSpotShadowColor() {
        return ambientShadowColor.getDefaultColor();
    }

    @Override
    public boolean hasShadow() {
        return false;
    }

    @Override
    public void drawShadow(Canvas canvas) {

    }

    @Override
    public void draw(Canvas canvas) {
        boolean c = !Carbon.isShapeRect(shapeModel, boundsRect);

        if (Carbon.IS_PIE_OR_HIGHER) {
            if (spotShadowColor != null)
                super.setOutlineSpotShadowColor(spotShadowColor.getColorForState(getDrawableState(), spotShadowColor.getDefaultColor()));
            if (ambientShadowColor != null)
                super.setOutlineAmbientShadowColor(ambientShadowColor.getColorForState(getDrawableState(), ambientShadowColor.getDefaultColor()));
        }

        // 判断如果不是圆角矩形,需要使用轮廓Path,绘制一下Path,不然显示会很奇怪
        if (getWidth() > 0 && getHeight() > 0 && ((c && !Carbon.IS_LOLLIPOP_OR_HIGHER) || !shapeModel.isRoundRect(boundsRect))) {
            int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
            drawInternal(canvas);
            paint.setXfermode(Carbon.CLEAR_MODE);
            if (c) {
                cornersMask.setFillType(Path.FillType.INVERSE_WINDING);
                canvas.drawPath(cornersMask, paint);
            }
            canvas.restoreToCount(saveCount);
            paint.setXfermode(null);
        }else{
            drawInternal(canvas);
        }
    }

    public void drawInternal(@NonNull Canvas canvas) {
        super.draw(canvas);
        if(stroke!=null){
            drawStroke(canvas);
        }
        if (rippleDrawable != null && rippleDrawable.getStyle() == RippleDrawable.Style.Over)
            rippleDrawable.draw(canvas);
    }

    // -------------------------------
    // shape
    // -------------------------------
    private ShapeAppearanceModel shapeModel = new ShapeAppearanceModel();
    private MaterialShapeDrawable shadowDrawable = new MaterialShapeDrawable(shapeModel);
    @Override
    public void setShapeModel(ShapeAppearanceModel shapeModel) {
        this.shapeModel = shapeModel;
        shadowDrawable = new MaterialShapeDrawable(shapeModel);
        if (getWidth() > 0 && getHeight() > 0)
            updateCorners();
        if (!Carbon.IS_LOLLIPOP_OR_HIGHER)
            postInvalidate();
    }
    // View的轮廓形状
    private RectF boundsRect = new RectF();
    // View的轮廓形状形成的Path路径
    private Path cornersMask = new Path();

    /**
     * 更新圆角
     */
    private void updateCorners() {
        if (Carbon.IS_LOLLIPOP_OR_HIGHER) {
            // 如果不是矩形,裁剪View的轮廓
            if (!Carbon.isShapeRect(shapeModel, boundsRect)){
                setClipToOutline(true);
            }
            //该方法返回一个Outline对象,它描述了该视图的形状。
            setOutlineProvider(new ViewOutlineProvider() {
                @Override
                public void getOutline(View view, Outline outline) {
                    if (Carbon.isShapeRect(shapeModel, boundsRect)) {
                        outline.setRect(0, 0, getWidth(), getHeight());
                    } else {
                        shadowDrawable.setBounds(0, 0, getWidth(), getHeight());
                        shadowDrawable.setShadowCompatibilityMode(MaterialShapeDrawable.SHADOW_COMPAT_MODE_NEVER);
                        shadowDrawable.getOutline(outline);
                    }
                }
            });
        }
        // 拿到圆角矩形的形状
        boundsRect.set(shadowDrawable.getBounds());
        // 拿到圆角矩形的Path
        shadowDrawable.getPathForSize(getWidth(), getHeight(), cornersMask);
    }

    @Override
    public ShapeAppearanceModel getShapeModel() {
        return this.shapeModel;
    }

    @Override
    public void setCornerCut(float cornerCut) {
        shapeModel = ShapeAppearanceModel.builder().setAllCorners(new CutCornerTreatment(cornerCut)).build();
        setShapeModel(shapeModel);
    }

    @Override
    public void setCornerRadius(float cornerRadius) {
        shapeModel = ShapeAppearanceModel.builder().setAllCorners(new RoundedCornerTreatment(cornerRadius)).build();
        setShapeModel(shapeModel);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        if (!changed)
            return;

        if (getWidth() == 0 || getHeight() == 0)
            return;

        updateCorners();
        if (rippleDrawable != null)
            rippleDrawable.setBounds(0, 0, getWidth(), getHeight());
    }

    // -------------------------------
    // ripple
    // -------------------------------

    private RippleDrawable rippleDrawable;

    @Override
    public boolean dispatchTouchEvent(@NonNull MotionEvent event) {

        if (rippleDrawable != null && event.getAction() == MotionEvent.ACTION_DOWN)
            rippleDrawable.setHotspot(event.getX(),event.getY());

        return super.dispatchTouchEvent(event);
    }

    @Override
    public RippleDrawable getRippleDrawable() {
        return rippleDrawable;
    }

    @Override
    public void setRippleDrawable(RippleDrawable newRipple) {
        if (rippleDrawable != null) {
            rippleDrawable.setCallback(null);
            if (rippleDrawable.getStyle() == RippleDrawable.Style.Background)
                super.setBackgroundDrawable(rippleDrawable.getBackground());
        }

        if (newRipple != null) {
            newRipple.setCallback(this);
            newRipple.setBounds(0, 0, getWidth(), getHeight());
            newRipple.setState(getDrawableState());
            ((Drawable) newRipple).setVisible(getVisibility() == VISIBLE, false);
            if (newRipple.getStyle() == RippleDrawable.Style.Background)
                super.setBackgroundDrawable((Drawable) newRipple);
        }

        rippleDrawable = newRipple;
    }

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        if (rippleDrawable != null && rippleDrawable.getStyle() != RippleDrawable.Style.Background)
            rippleDrawable.setState(getDrawableState());
    }

    @Override
    protected boolean verifyDrawable(@NonNull Drawable who) {
        return super.verifyDrawable(who) || rippleDrawable == who;
    }

    @Override
    public void invalidateDrawable(@NonNull Drawable drawable) {
        super.invalidateDrawable(drawable);
        invalidateParentIfNeeded();
    }

    @Override
    public void invalidate(@NonNull Rect dirty) {
        super.invalidate(dirty);
        invalidateParentIfNeeded();
    }

    @Override
    public void invalidate(int l, int t, int r, int b) {
        super.invalidate(l, t, r, b);
        invalidateParentIfNeeded();
    }

    @Override
    public void invalidate() {
        super.invalidate();
        invalidateParentIfNeeded();
    }

    private void invalidateParentIfNeeded() {
        if (getParent() == null || !(getParent() instanceof View))
            return;

        if (rippleDrawable != null && rippleDrawable.getStyle() == RippleDrawable.Style.Borderless)
            ((View) getParent()).invalidate();
    }

    @Override
    public void setBackground(Drawable background) {
        setBackgroundDrawable(background);
    }

    @Override
    public void setBackgroundDrawable(Drawable background) {
        if (background instanceof RippleDrawable) {
            setRippleDrawable((RippleDrawable) background);
            return;
        }

        if (rippleDrawable != null && rippleDrawable.getStyle() == RippleDrawable.Style.Background) {
            rippleDrawable.setCallback(null);
            rippleDrawable = null;
        }
        super.setBackgroundDrawable(background);
    }

    // -------------------------------
    // stroke
    // -------------------------------

    private ColorStateList stroke;
    private float strokeWidth;
    private Paint strokePaint;

    private void drawStroke(Canvas canvas) {
        strokePaint.setStrokeWidth(strokeWidth * 2);
        strokePaint.setColor(stroke.getColorForState(getDrawableState(), stroke.getDefaultColor()));
        cornersMask.setFillType(Path.FillType.WINDING);
        canvas.drawPath(cornersMask, strokePaint);
    }

    @Override
    public void setStroke(ColorStateList colorStateList) {
        stroke = colorStateList;

        if (stroke == null)
            return;

        if (strokePaint == null) {
            strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            strokePaint.setStyle(Paint.Style.STROKE);
        }
    }

    @Override
    public void setStroke(int color) {
        setStroke(ColorStateList.valueOf(color));
    }

    @Override
    public ColorStateList getStroke() {
        return stroke;
    }

    @Override
    public void setStrokeWidth(float strokeWidth) {
        this.strokeWidth = strokeWidth;
    }

    @Override
    public float getStrokeWidth() {
        return strokeWidth;
    }

}

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

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

相关文章

桌面动态壁纸

1、将压缩包中解压&#xff0c;选择所需要的壁纸复制到目录 2、解压kugou动态壁纸运行程序&#xff0c;文件选择默认打开文件为animation_desktop_webgl.exe 3、重启电脑生效

【总结】kubernates 插件工具总结

在此记录工作中用到的关于 kubernates 的插件小工具&#xff0c;以防以后忘记 1、能显示 kubernates 所处上下文的插件 kube-ps1 github 地址&#xff1a; https://github.com/jonmosco/kube-ps1 效果 2、能方便切换 kubernates 上下文的插件 kubecm github 地址&#xff1…

Python中跨越多个文件使用全局变量

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 这个琐碎的指南是关于在 Python 中跨多个文件使用全局变量。 但是在进入主题之前&#xff0c;让我们简单地看看全局变量和它们在多个文件中的用途。 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff…

流程引擎概述及组成

一、流程引擎概述 流程&#xff0c;可以理解为步骤&#xff0c;一个有序的活动或动作&#xff1b; 引擎&#xff0c;可以理解为驱动&#xff0c;是一个程序或者一套系统。 所以&#xff0c;字面意思可以理解为&#xff0c;流程引擎是一套&#xff08;或一个&#xff09;用来…

Java 线程的生命周期

&#x1f648;作者简介&#xff1a;练习时长两年半的Java up主 &#x1f649;个人主页&#xff1a;程序员老茶 &#x1f64a; ps:点赞&#x1f44d;是免费的&#xff0c;却可以让写博客的作者开兴好久好久&#x1f60e; &#x1f4da;系列专栏&#xff1a;Java全栈&#xff0c;…

笔记38:膨胀卷积/空洞卷积的原理

本地笔记&#xff1a;D:\work_file\DeepLearning_Learning\03_个人笔记\膨胀卷积 a a a a a a a a a a a a a a a

如何为金蝶ERP软件配置自定义域名,并实现公网访问——“cpolar内网穿透”

文章目录 前言1. 保留自定义域名2. 域名解析3. 配置自定义域名4. 关于服务器选择以及域名备案的说明4.1 关于服务器地区的选择&#xff1a;4.2 关于自定义域名备案&#xff1a;4.3 关于域名过白名单&#xff1a; 前言 上篇文章我们讲过如何安装金蝶云星空&#xff0c;实现异地…

JavaScript语法(一)

一、什么是JavaScript 1、DOM 文档对象模型&#xff08;Document Object Model&#xff09;是一个应用编程接口&#xff08;API&#xff09;&#xff0c;用于在HTML中使用扩展的XML&#xff0c;DOM 将整个页面抽象为一组分层节点。HTML 或 XML 页面的每个组成部分都是一种节点…

线程安全问题 --- 死锁

文章目录 线程安全产生线程安全问题的原因上锁synchronized使用方法1. 修饰方法2. 修饰代码块synchronized的可重入性死锁什么是死锁死锁的三个典型情况:死锁的四个必要条件如何破除(避免)死锁线程安全类线程安全 由于多个线程之间是抢占式执行的, 这就给调度执行顺序带来了随…

Redis(三)

文章目录 一、单节点Redis的问题&#xff08;一&#xff09;数据丢失&#xff08;二&#xff09;并发能力问题&#xff08;三&#xff09;存储能力问题&#xff08;四&#xff09;故障恢复问题 二、Redis持久化&#xff08;一&#xff09;RDB1、RDB是什么2、rdb配置3、手动触发…

Java基于SpringBoot的高校招生系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 简介系统设计思路1 数据库设计2 系统整体设计 系统详细设计1系统功能模块2. 管理员功能模块3学生…

时序预测 | MATLAB实现ICEEMDAN-IMPA-LSTM时间序列预测

时序预测 | MATLAB实现ICEEMDAN-IMPA-LSTM时间序列预测 目录 时序预测 | MATLAB实现ICEEMDAN-IMPA-LSTM时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 ICEEMDAN-IMPA-LSTM功率/风速预测 基于改进的自适应经验模态分解改进海洋捕食者算法长短期记忆网络时间序…

MQTT协议入门介绍

前言 物联网曾被认为是继计算机、互联网之后&#xff0c;信息技术行业的第三次浪潮。随着基础通讯设施的不断完善&#xff0c;尤其是 5G 的出现&#xff0c;进一步降低了万物互联的门槛和成本。物联网本身也是 AI 和区块链应用很好的落地场景之一&#xff0c;各大云服务商也在…

【漏洞复现】某 NVR 视频存储管理设备远程命令执行

漏洞描述 NUUO NVR是中国台湾NUUO公司旗下的一款网络视频记录器&#xff0c;该设备存在远程命令执行漏洞&#xff0c;攻击者可利用该漏洞执行任意命令&#xff0c;进而获取服务器的权限。 免责声明 技术文章仅供参考&#xff0c;任何个人和组织使用网络应当遵守宪法法律&am…

简单对比一下 C 与 Go 两种语言

使用一个简单的计数程序将古老的 C 语言与现代 Go 进行比较。 Go 是一种现代编程语言&#xff0c;追溯其历史大部分源自编程语言 C。所以&#xff0c;任何熟悉 C 语言的开发者都可能会觉得 Go 很熟悉。C 程序员使用 Go 编写新程序变得容易&#xff0c;同时避免了 C 编程语言的…

安全典型配置(一)使用ACL限制FTP访问权限案例

【微|信|公|众|号&#xff1a;厦门微思网络】 使用ACL限制FTP访问权限案例 ACL简介 访问控制列表ACL&#xff08;Access Control List&#xff09;是由一条或多条规则组成的集合。所谓规则&#xff0c;是指描述报文匹配条件的判断语句&#xff0c;这些条件可以是报文的源地址…

申请免费 SSL 证书为您的小程序加密通信

在今天的网络环境中&#xff0c;数据安全和隐私保护变得尤为重要。无论是网站还是应用程序&#xff0c;为其提供安全的通信渠道都是至关重要的。对于小程序开发者来说&#xff0c;使用 SSL&#xff08;Secure Sockets Layer&#xff09;证书可以有效地保障用户数据的安全&#…

[架构之路-235]:目标系统 - 纵向分层 - 数据库 - 数据库系统基础与概述(快速入门、了解核心概念):概念模型、逻辑模型、物理模型

目录 一、核心概念 1.1 什么是数据与信息 1.2 数据与数据库的关系 1.3 什么是数据库 1.4 数据库中的数据的特点 1.5 数据库与数据结构的关系 1.6 什么是数据库管理系统 1.7 什么是数据库系统 1.8 数据库的主要功能 1.9 Excel表格是数据库吗&#xff1f; 1.10 Excel表…

Asymmetric channel bandwidths(非对称信道带宽)

一人问&#xff1a;“你知道非对称信道带宽&#xff0c;XXX支持吗&#xff1f;”一人答&#xff1a;“我打开XXX的RF应答表看看”。 我默默打开了38.306搜了一下&#xff0c;简单研究了下。 在R15 中&#xff0c;为某些NR band引入了asymmetric Bandwidth UL/DL&#xff0c;以优…

Python机器学习实战-特征重要性分析方法(9):卡方检验(附源码和实现效果)

实现功能 使用chi2()获得每个特征的卡方统计信息。得分越高的特征越有可能独立于目标。 实现代码 from sklearn.feature_selection import chi2 import pandas as pd from sklearn.datasets import load_breast_cancer import matplotlib.pyplot as pltX, y load_breast_ca…