Java自定义生成二维码(兼容你所有的需求)

news2024/11/27 1:28:54

1、概述

作为Java开发人员,说到生成二维码就会想到zxing开源二维码图像处理库,不可否认的是zxing确实很强大,但是实际需求中会遇到各种各样的需求是zxing满足不了的,于是就有了想法自己扩展zxing满足历史遇到的各种需求,经过3周的研究学习+开发,兼容你所有需求的Java二维码生成器孕育而生,接下来我们就看看我写的这个二维码生成器都实现了哪些功能。

2、已实现需求

  • zxing支持的二维码设置

  • 自定义logo,自动增加白边框、圆形、圆角矩形

  • 最终二维码圆角矩形生成

  • 自定义背景颜色

  • 自定义背景图片,可以设置二维码放置背景图片的位置

  • 自定义设置二维码顶部注释,自定义字体、字体颜色、背景颜色,居中、自动换行

  • 自定义设置二维码底部注释,自定义字体、字体颜色、背景颜色,居中、自动换行

  • 设置码点颜色、形状(普通、原型、方形、三角形、圆角矩形、菱形)

  • 设置码眼外框颜色、形状(方形、圆形-粗边、圆形-细边、圆角矩形-粗边、圆角矩形-中边、圆角矩形-细边)

  • 设置码眼内颜色、形状(方形、圆形、圆角矩形、三角形、菱形)

  • 其他继续扩展中

3、项目配置详细说明

3.1、二维码背景配置

package com.faea.qrcode.config;

import lombok.Getter;

import java.awt.*;

/**
 * 二维码背景配置
 *
 * @author liuchao
 * @date 2023/2/17
 */
@Getter
public class CodeBackgroundConfig {

    public CodeBackgroundConfig() {
        this.color = Color.WHITE;
    }


    /**
     * 背景色,默认白色
     */
    public Color color;

    /**
     * 二维码绘制在背景图上的X轴
     */
    public int codeDrawX;

    /**
     * 二维码绘制在北京图上的Y轴
     */
    public int codeDrawY;

    /**
     * 背景图片
     */
    private Image image;

    public CodeBackgroundConfig color(Color color) {
        this.color = color;
        return this;
    }

    public CodeBackgroundConfig image(Image image) {
        this.image = image;
        return this;
    }

    public CodeBackgroundConfig codeDrawX(Integer codeDrawX) {
        this.codeDrawX = codeDrawX;
        return this;
    }

    public CodeBackgroundConfig codeDrawY(Integer codeDrawY) {
        this.codeDrawY = codeDrawY;
        return this;
    }
}

3.2、二维码码点配置

package com.faea.qrcode.config;

import com.faea.qrcode.constants.CodeDotShapeEnum;
import lombok.Getter;

import java.awt.*;

/**
 * 二维码码点配置
 *
 * @author liuchao
 * @date 2023/2/17
 */
@Getter
public class CodeDotConfig {

    public CodeDotConfig() {
        this.color = Color.BLACK;
        this.shape = CodeDotShapeEnum.NORMAL;
    }

    /**
     * 码点颜色
     */
    private Color color;

    /**
     * 形状枚举
     */
    private CodeDotShapeEnum shape;

    public CodeDotConfig shape(CodeDotShapeEnum shape) {
        this.shape = shape;
        return this;
    }

    public CodeDotConfig color(Color color) {
        this.color = color;
        return this;
    }
}

3.3、二维码码眼内配置

package com.faea.qrcode.config;

import com.faea.qrcode.constants.CodeEyeInsideShapeEnum;
import lombok.Getter;

import java.awt.*;

/**
 * 二维码码眼内配置
 *
 * @author liuchao
 * @date 2023/2/19
 */
@Getter
public class CodeEyeInsideConfig {
    public CodeEyeInsideConfig() {
        this(Color.BLACK, CodeEyeInsideShapeEnum.SQUARE);
    }

    public CodeEyeInsideConfig(Color color, CodeEyeInsideShapeEnum shape) {
        this.color = color;
        this.shape = shape;
    }

    /**
     * 颜色
     */
    private Color color;

    /**
     * 形状
     */
    private CodeEyeInsideShapeEnum shape;

    public CodeEyeInsideConfig color(Color color) {
        this.color = color;
        return this;
    }

    public CodeEyeInsideConfig shape(CodeEyeInsideShapeEnum shape) {
        this.shape = shape;
        return this;
    }
}

3.4、二维码码眼外配置

package com.faea.qrcode.config;

import com.faea.qrcode.constants.CodeEyeOutShapeEnum;
import lombok.Getter;

import java.awt.*;

/**
 * 二维码码眼外配置
 *
 * @author liuchao
 * @date 2023/2/19
 */
@Getter
public class CodeEyeOutConfig {
    public CodeEyeOutConfig() {
        this(Color.BLACK, CodeEyeOutShapeEnum.SQUARE);
    }

    public CodeEyeOutConfig(Color color, CodeEyeOutShapeEnum shape) {
        this.color = color;
        this.shape = shape;
    }

    /**
     * 颜色
     */
    private Color color;

    /**
     * 形状
     */
    private CodeEyeOutShapeEnum shape;

    public CodeEyeOutConfig color(Color color) {
        this.color = color;
        return this;
    }

    public CodeEyeOutConfig shape(CodeEyeOutShapeEnum shape) {
        this.shape = shape;
        return this;
    }
}

3.5、二维码LOGO配置

package com.faea.qrcode.config;
import com.faea.qrcode.constants.CodeLogoShapeEnum;
import lombok.Data;

import java.awt.*;
import java.io.File;

/**
 * 二维码LOGO配置
 *
 * @author liuchao
 * @date 2023/2/17
 */
@Data
public class CodeLogoConfig {
    public CodeLogoConfig() {
        this(null);
    }

    public CodeLogoConfig(Image image) {
        this.image = image;
        this.scale = 0.1F;
        this.shape = CodeLogoShapeEnum.ROUNDED;
        this.alpha = 1F;
    }

    /**
     * LOGO 图片
     */
    private Image image;

    /**
     * 形状
     */
    private CodeLogoShapeEnum shape;

    /**
     * LOGO 透明度
     */
    private Float alpha;

    /**
     * 缩放比例。比例大于1时为放大,小于1大于0为缩小
     */
    private float scale;

    /**
     * 设置二维码中的Logo文件
     *
     * @param logoPath 二维码中的Logo路径
     * @return this;
     */
    public CodeLogoConfig image(String logoPath) {
        return image(FileUtil.file(logoPath));
    }

    /**
     * 设置二维码中的Logo文件
     *
     * @param logoFile 二维码中的Logo
     * @return this;
     */
    public CodeLogoConfig image(File logoFile) {
        return image(ImgUtil.read(logoFile));
    }

    /**
     * 设置二维码中的Logo
     *
     * @param image 二维码中的Logo
     * @return this;
     */
    public CodeLogoConfig image(Image image) {
        this.image = image;
        return this;
    }

    public CodeLogoConfig shape(CodeLogoShapeEnum shape) {
        this.shape = shape;
        return this;
    }

    public CodeLogoConfig alpha(Float alpha) {
        this.alpha = alpha;
        return this;
    }

    public CodeLogoConfig scale(float scale) {
        this.scale = scale;
        return this;
    }
}

3.6、二维码注释配置

package com.faea.qrcode.config;

import lombok.Data;

import java.awt.*;

/**
 * 二维码注释配置
 *
 * @author liuchao
 * @date 2023/2/17
 */
@Data
public class CodeNoteConfig {
    public CodeNoteConfig() {
        this.rowSpacing = 5;
        this.size = 14;
        this.font = "SimHei";
        this.backColor = Color.DARK_GRAY;
        this.fontColor = Color.BLACK;
    }


    /**
     * 注释内容
     */
    private String content;

    /**
     * 注释字体
     */
    private String font;

    /**
     * 字体大小
     */
    private Integer size;

    /**
     * 背景颜色
     */
    private Color backColor;

    /**
     * 字体颜色
     */
    private Color fontColor;

    /**
     * 行间距
     */
    private Integer rowSpacing;

    public CodeNoteConfig backColor(Color color) {
        this.backColor = color;
        return this;
    }

    public CodeNoteConfig fontColor(Color color) {
        this.fontColor = color;
        return this;
    }

    public CodeNoteConfig content(String content) {
        this.content = content;
        return this;
    }

    public CodeNoteConfig font(String font) {
        this.font = font;
        return this;
    }

    public CodeNoteConfig size(Integer size) {
        this.size = size;
        return this;
    }

    public CodeNoteConfig rowSpacing(Integer rowSpacing) {
        this.rowSpacing = rowSpacing;
        return this;
    }
}

3.7、总配置入口类

package com.faea.qrcode.config;

import cn.hutool.core.util.CharsetUtil;
import com.google.zxing.EncodeHintType;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.Getter;

import java.nio.charset.Charset;
import java.util.HashMap;

/**
 * 二维码总配置
 *
 * @author liuchao
 * @date 2023/2/17
 */
@Getter
public class CodeConfig {


    /**
     * 创建QrConfig
     *
     * @return QrConfig
     * @since 4.1.14
     */
    public static CodeConfig create() {
        return new CodeConfig();
    }

    /**
     * 构造,默认长宽为300
     */
    public CodeConfig() {
        this(500, 500);
    }

    /**
     * 构造
     *
     * @param width  宽
     * @param height 长
     */
    public CodeConfig(int width, int height) {
        this.width = width;
        this.height = height;
        this.margin = 1;
        this.dot = new CodeDotConfig();
        this.background = new CodeBackgroundConfig();
        this.eyeOut = new CodeEyeOutConfig();
        this.eyeInside = new CodeEyeInsideConfig();
        this.errorCorrection = ErrorCorrectionLevel.H;
        this.charset = CharsetUtil.CHARSET_UTF_8;
        this.arc = 1D;
    }


    /**
     * 宽
     */
    private Integer width;
    /**
     * 长
     */
    private Integer height;

    /**
     * 边距1~4
     * 二维码空白区域,最小为0也有白边,只是很小,最小是6像素左右
     */
    private Integer margin;

    /**
     * 码眼外
     */
    private CodeEyeOutConfig eyeOut;

    /**
     * 码眼内
     */
    private CodeEyeInsideConfig eyeInside;

    /**
     * 纠错级别
     */
    private ErrorCorrectionLevel errorCorrection;
    /**
     * 编码
     */
    private Charset charset;

    /**
     * 前景色配置
     */
    private CodeDotConfig dot;

    /**
     * 背景色配置
     */
    private CodeBackgroundConfig background;

    /**
     * 圆角弧度,0~1,为长宽占比
     * 设置最终二维码 圆角弧度
     * 1:原图
     * 0.3D:圆角矩形
     */
    private Double arc;

    /**
     * logo
     */
    private CodeLogoConfig logo;

    /**
     * 顶部注释
     */
    private CodeNoteConfig topNote;

    /**
     * 底部注释
     */
    private CodeNoteConfig bottomNote;

    public CodeConfig logo(CodeLogoConfig logo) {
        this.logo = logo;
        return this;
    }

    /**
     * 转换为Zxing的二维码配置
     *
     * @return 配置
     */
    public HashMap<EncodeHintType, Object> toHints() {
        // 配置
        final HashMap<EncodeHintType, Object> hints = new HashMap<>();
        if (null != this.charset) {
            hints.put(EncodeHintType.CHARACTER_SET, charset.toString().toLowerCase());
        }
        if (null != this.errorCorrection) {
            hints.put(EncodeHintType.ERROR_CORRECTION, this.errorCorrection);
        }
        if (null != this.margin) {
            hints.put(EncodeHintType.MARGIN, this.margin);
        }
        return hints;
    }

    public CodeConfig width(Integer width) {
        this.width = width;
        return this;
    }

    public CodeConfig height(Integer height) {
        this.height = height;
        return this;
    }

    public CodeConfig margin(Integer margin) {
        this.margin = margin;
        return this;
    }

    public CodeConfig eyeOut(CodeEyeOutConfig eyeOut) {
        this.eyeOut = eyeOut;
        return this;
    }

    public CodeConfig eyeInside(CodeEyeInsideConfig eyeInside) {
        this.eyeInside = eyeInside;
        return this;
    }

    public CodeConfig errorCorrection(ErrorCorrectionLevel errorCorrection) {
        this.errorCorrection = errorCorrection;
        return this;
    }

    public CodeConfig charset(Charset charset) {
        this.charset = charset;
        return this;
    }

    public CodeConfig dot(CodeDotConfig dot) {
        this.dot = dot;
        return this;
    }

    public CodeConfig background(CodeBackgroundConfig background) {
        this.background = background;
        return this;
    }


    public CodeConfig topNote(CodeNoteConfig topNote) {
        this.topNote = topNote;
        return this;
    }

    public CodeConfig bottomNote(CodeNoteConfig bottomNote) {
        this.bottomNote = bottomNote;
        return this;
    }

    public CodeConfig arc(Double arc) {
        this.arc = arc;
        return this;
    }
}

3.8、核心工具类

package com.faea.qrcode.util;

import com.faea.qrcode.config.*;
import com.faea.qrcode.constants.CodeEyePositionEnum;
import com.faea.qrcode.constants.CodeLogoShapeEnum;
import com.faea.qrcode.model.CodeDotDrawModel;
import com.faea.qrcode.model.CodeEyeInsideDrawModel;
import com.faea.qrcode.model.CodeEyeOutDrawModel;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import lombok.Data;
import lombok.extern.java.Log;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.OutputStream;

/**
 * 二维码工具类
 *
 * @author liuchao
 * @date 2023/2/17
 */
@Log
public class CodeUtil {


    /**
     * 生成代 logo 图片的 Base64 编码格式的二维码,以 String 形式表示
     *
     * @param content    内容
     * @param config     二维码配置,包括长、宽、边距、颜色等
     * @param imageType  图片类型(图片扩展名),见{@link ImgUtil}
     * @param logoBase64 logo 图片的 base64 编码
     * @return 图片 Base64 编码字符串
     */
    public static String generateAsBase64(String content, CodeConfig config, String imageType, String logoBase64) {
        return generateAsBase64(content, config, imageType, Base64.decode(logoBase64));
    }

    /**
     * 生成代 logo 图片的 Base64 编码格式的二维码,以 String 形式表示
     *
     * @param content   内容
     * @param config    二维码配置,包括长、宽、边距、颜色等
     * @param imageType 图片类型(图片扩展名),见{@link ImgUtil}
     * @param logo      logo 图片的byte[]
     * @return 图片 Base64 编码字符串
     */
    public static String generateAsBase64(String content, CodeConfig config, String imageType, byte[] logo) {
        return generateAsBase64(content, config, imageType, ImgUtil.toImage(logo));
    }

    /**
     * 生成代 logo 图片的 Base64 编码格式的二维码,以 String 形式表示
     *
     * @param content   内容
     * @param config    二维码配置,包括长、宽、边距、颜色等
     * @param imageType 图片类型(图片扩展名),见{@link ImgUtil}
     * @param logo      logo 图片的byte[]
     * @return 图片 Base64 编码字符串
     */
    public static String generateAsBase64(String content, CodeConfig config, String imageType, Image logo) {
        config.getLogo().image(logo);
        return generateAsBase64(content, config, imageType);
    }

    /**
     * 生成 Base64 编码格式的二维码,以 String 形式表示
     *
     * <p>
     * 输出格式为: data:image/[type];base64,[data]
     * </p>
     *
     * @param content   内容
     * @param config    二维码配置,包括长、宽、边距、颜色等
     * @param imageType 图片类型(图片扩展名),见{@link ImgUtil}
     * @return 图片 Base64 编码字符串
     */
    public static String generateAsBase64(String content, CodeConfig config, String imageType) {
        final BufferedImage img = generate(content, config);
        return ImgUtil.toBase64DateUri(img, imageType);
    }

    /**
     * 生成PNG格式的二维码图片,以byte[]形式表示
     *
     * @param content 内容
     * @param width   宽度
     * @param height  高度
     * @return 图片的byte[]
     */
    public static byte[] generatePng(String content, int width, int height) {
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        generate(content, width, height, ImgUtil.IMAGE_TYPE_PNG, out);
        return out.toByteArray();
    }

    /**
     * 生成PNG格式的二维码图片,以byte[]形式表示
     *
     * @param content 内容
     * @param config  二维码配置,包括长、宽、边距、颜色等
     * @return 图片的byte[]
     */
    public static byte[] generatePng(String content, CodeConfig config) {
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        generate(content, config, ImgUtil.IMAGE_TYPE_PNG, out);
        return out.toByteArray();
    }

    /**
     * 生成二维码到文件,二维码图片格式取决于文件的扩展名
     *
     * @param content    文本内容
     * @param width      宽度
     * @param height     高度
     * @param targetFile 目标文件,扩展名决定输出格式
     * @return 目标文件
     */
    public static File generate(String content, int width, int height, File targetFile) {
        final BufferedImage image = generate(content, width, height);
        ImgUtil.write(image, targetFile);
        return targetFile;
    }

    /**
     * 生成二维码到文件,二维码图片格式取决于文件的扩展名
     *
     * @param content    文本内容
     * @param width      宽度
     * @param height     高度
     * @param logo       logo图片
     * @param targetFile 目标文件,扩展名决定输出格式
     * @return 目标文件
     */
    public static File generate(String content, int width, int height, Image logo, File targetFile) {
        CodeConfig config = new CodeConfig(width, height).logo(new CodeLogoConfig(logo));
        final BufferedImage image = generate(content, config);
        ImgUtil.write(image, targetFile);
        return targetFile;
    }

    /**
     * 生成二维码到输出流
     *
     * @param content   文本内容
     * @param width     宽度
     * @param height    高度
     * @param imageType 图片类型(图片扩展名),见{@link ImgUtil}
     * @param out       目标流
     */
    public static void generate(String content, int width, int height, String imageType, OutputStream out) {
        final BufferedImage image = generate(content, width, height);
        ImgUtil.write(image, imageType, out);
    }

    /**
     * 生成二维码到输出流
     *
     * @param content   文本内容
     * @param config    二维码配置,包括长、宽、边距、颜色等
     * @param imageType 图片类型(图片扩展名),见{@link ImgUtil}
     * @param out       目标流
     */
    public static void generate(String content, CodeConfig config, String imageType, OutputStream out) {
        final BufferedImage image = generate(content, config);
        ImgUtil.write(image, imageType, out);
    }

    /**
     * 生成二维码图片
     *
     * @param content 文本内容
     * @param width   宽度
     * @param height  高度
     * @return 二维码图片(黑白)
     */
    public static BufferedImage generate(String content, int width, int height) {
        return generate(content, new CodeConfig(width, height));
    }

    /**
     * 生成二维码到文件,二维码图片格式取决于文件的扩展名
     *
     * @param content    文本内容
     * @param config     二维码配置,包括长、宽、边距、颜色等
     * @param targetFile 目标文件,扩展名决定输出格式
     * @return 目标文件
     */
    public static File generate(String content, CodeConfig config, File targetFile) {
        final BufferedImage image = generate(content, config);
        ImgUtil.write(image, targetFile);
        return targetFile;
    }


    /**
     * 生成二维码图片
     *
     * @param content 二维码内容
     * @param config  二维码配置
     * @return java.awt.image.BufferedImage
     * @author liuchao
     * @date 2023/2/17
     */
    public static BufferedImage generate(String content, CodeConfig config) {
        BitMatrix bitMatrix;
        try {
            bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE,
                    config.getWidth(), config.getHeight(), config.toHints());
        } catch (WriterException e) {
            throw new RuntimeException(e);
        }

        //获取二维码图片尺寸信息
        CodeImageSizeInfo sizeInfo = convertCodeImageSizeInfo(config);

        BufferedImage image = toImage(bitMatrix, sizeInfo, config);

        //绘制背景图片
        image = drawBackgroundImage(image, config, sizeInfo);

        //绘制logo
        drawLogo(image, config);

        //圆角处理
        if (config.getArc() < 1D) {
            image = ImgUtil.toBufferedImage(Img.from(image).round(config.getArc()).getImg());
        }

        return image;
    }


    /**
     * 通过内容配置转换为字体
     *
     * @param note
     * @return java.awt.FontMetrics
     * @author liuchao
     * @date 2023/2/17
     */
    private static FontMetrics convertFont(CodeNoteConfig note) {
        if (null == note) {
            return null;
        }

        Font font = new Font(note.getFont(), Font.PLAIN, note.getSize());
        JLabel jLabel = new JLabel(note.getContent());
        //第一次获取非常慢,建议在应用启动的时候加载一次
        FontMetrics fontMetrics = jLabel.getFontMetrics(font);
        return fontMetrics;
    }

    /**
     * BitMatrix转BufferedImage
     *
     * @param matrix   BitMatrix
     * @param sizeInfo 二维码尺寸信息
     * @param config
     * @return java.awt.image.BufferedImage
     * @author liuchao
     * @date 2023/2/17
     */
    private static BufferedImage toImage(BitMatrix matrix, CodeImageSizeInfo sizeInfo, CodeConfig config) {
        final int width = matrix.getWidth();

        //创建二维码最终Image
        BufferedImage image = new BufferedImage(width, sizeInfo.getCodeImageTotalHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D graphics2D = ImageUtil.imageToGraphics2D(image);

        //绘制背景
        drawBackgroundColor(graphics2D, sizeInfo, config, matrix);

        // 处理上、下注释、码点、码眼
        drawNoteAndDotEye(graphics2D, sizeInfo, config, matrix);

        //添加注释
        drawNote(graphics2D, sizeInfo, config);

        graphics2D.dispose();
        image.flush();
        return image;
    }


    /**
     * 绘制logo
     *
     * @param image
     * @param config
     * @return void
     * @author liuchao
     * @date 2023/2/18
     */
    private static void drawLogo(BufferedImage image, CodeConfig config) {
        if (null == config.getLogo() || null == config.getLogo().getImage()) {
            return;
        }

        CodeLogoConfig logoConfig = config.getLogo();
        //圆角弧度,0~1,为长宽占比
        Double arc = logoConfig.getShape().getArc();
        //缩放
        BufferedImage srcImage = ImgUtil.toBufferedImage(Img.from(logoConfig.getImage())
                .scale(logoConfig.getScale()).round(arc).getImg());
        //增加边框 非原型 则增加边框
        if (!logoConfig.getShape().equals(CodeLogoShapeEnum.ORIGINAL)) {
            srcImage = ImageUtil.roundBorder(srcImage, arc);
        }
        ImgUtil.pressImage(image, srcImage, new Rectangle(srcImage.getWidth(), srcImage.getHeight()), logoConfig.getAlpha());
    }


    /**
     * 增加注释
     *
     * @param graphics2D
     * @param sizeInfo
     * @param config
     * @return void
     * @author liuchao
     * @date 2023/2/18
     */
    private static void drawNote(Graphics2D graphics2D, CodeImageSizeInfo sizeInfo, CodeConfig config) {
        if (sizeInfo.getTopNoteHeight() == 0 && sizeInfo.getBottomNoteHeight() == 0) {
            return;
        }

        int startWriteY = 0;
        //顶部注释
        if (sizeInfo.getTopNoteHeight() > 0) {
            CodeNoteConfig topNode = config.getTopNote();
            drawHorizontalWords(graphics2D, startWriteY, topNode.getContent(), sizeInfo.topNoteFontMetrics, topNode.getFontColor(),
                    config.getWidth(), sizeInfo.getTopNoteRowSize(),
                    topNode.getRowSpacing());
            startWriteY += sizeInfo.getTopNoteHeight();
        }
        //底部注释
        if (sizeInfo.getBottomNoteHeight() > 0) {
            startWriteY += config.getHeight();
            CodeNoteConfig bottomNote = config.getBottomNote();
            drawHorizontalWords(graphics2D, startWriteY, bottomNote.getContent(), sizeInfo.bottomNoteFontMetrics, bottomNote.getFontColor(),
                    config.getWidth(), sizeInfo.getTopNoteRowSize(),
                    bottomNote.getRowSpacing());
        }
    }

    /**
     * 在Graphics2D 写横向文字
     *
     * @param graphics2D
     * @param startWriteY
     * @param content
     * @param fontMetrics
     * @param fontColor   字体颜色
     * @param qrCodeWidth
     * @param rowSize
     * @param rowSpacing
     * @return void
     * @author liuchao
     * @date 2023/2/18
     */
    private static void drawHorizontalWords(Graphics2D graphics2D, int startWriteY, String content,
                                            FontMetrics fontMetrics, Color fontColor, int qrCodeWidth,
                                            int rowSize, int rowSpacing) {
        graphics2D.setFont(fontMetrics.getFont());
        graphics2D.setColor(fontColor);
        int rowHeight = fontMetrics.getHeight();
        //当前写入Y轴
        int curWriteY = startWriteY + rowHeight;
        if (rowSize > 1) {
            //当前行字符
            StringBuffer curRowWords = new StringBuffer();
            for (int i = 0; i < content.length(); i++) {
                curRowWords.append(content.charAt(i));
                if (i == (content.length() - 1)) {
                    int wordWidth = fontMetrics.stringWidth(curRowWords.toString());
                    int writeX = (qrCodeWidth - wordWidth) / 2;
                    graphics2D.drawString(curRowWords.toString(), writeX, curWriteY);
                } else {
                    //判断当前已累加字符+下一个字符  长度是否大于 总宽度
                    StringBuffer tempWords = new StringBuffer(curRowWords).append(content.charAt(i + 1));
                    if (fontMetrics.stringWidth(tempWords.toString()) > qrCodeWidth) {
                        int wordWidth = fontMetrics.stringWidth(curRowWords.toString());
                        int writeX = (qrCodeWidth - wordWidth) / 2;
                        graphics2D.drawString(curRowWords.toString(), writeX, curWriteY);
                        curWriteY += rowHeight + rowSpacing;
                        curRowWords.delete(0, curRowWords.length());
                        //最后一个字符了
                    }
                }
            }
        } else {
            int wordWidth = fontMetrics.stringWidth(content);
            int writeX = (qrCodeWidth - wordWidth) / 2;
            graphics2D.drawString(content, writeX, curWriteY);
        }
    }

    /**
     * 绘制注释+码点+码眼
     *
     * @param graphics2D
     * @param sizeInfo
     * @param config
     * @param matrix
     * @return void
     * @author liuchao
     * @date 2023/2/22
     */
    private static void drawNoteAndDotEye(Graphics2D graphics2D,
                                          CodeImageSizeInfo sizeInfo, CodeConfig config, BitMatrix matrix) {
        CodeDotConfig dot = config.getDot();
        final int height = matrix.getHeight();
        final int width = matrix.getWidth();


        Boolean leftTopOut = false;
        Boolean rightTopOut = false;
        Boolean leftBottomOut = false;
        Boolean leftTopInside = false;
        Boolean rightTopInside = false;
        Boolean leftBottomInside = false;

        //上注释
        if (sizeInfo.getTopNoteHeight() > 0) {
            CodeNoteConfig topNote = config.getTopNote();
            graphics2D.setColor(topNote.getBackColor());
            graphics2D.fillRect(0, 0, width, sizeInfo.getTopNoteHeight());
        }

        //下注释
        if (sizeInfo.getBottomNoteHeight() > 0) {
            CodeNoteConfig bottomNote = config.getBottomNote();
            graphics2D.setColor(bottomNote.getBackColor());
            graphics2D.fillRect(0, sizeInfo.getTopNoteHeight() + height, width, sizeInfo.getBottomNoteHeight());
        }

        int multiple = matrix.getMultiple();
        int leftPadding = matrix.getLeftPadding();
        int topPadding = matrix.getTopPadding();

        ByteMatrix input = matrix.getInputMatrix();
        for (int inputY = 0, outputY = topPadding; inputY < input.getHeight(); inputY++, outputY += multiple) {
            // Write the contents of this row of the barcode
            for (int inputX = 0, outputX = leftPadding; inputX < input.getWidth(); inputX++, outputX += multiple) {
                if (input.get(inputX, inputY) == 1) {
                    int y = outputY + sizeInfo.getTopNoteHeight();
                    // 左上角码眼,外边框
                    if (input.getEyeOutPoint(CodeEyePositionEnum.LEFT_TOP, inputX, inputY) == 1) {
                        if (!leftTopOut) {
                            config.getEyeOut().getShape().draw(graphics2D, CodeEyeOutDrawModel.create(config.getEyeOut().getColor(), outputX, y, multiple));
                            leftTopOut = true;
                        }
                        //右上角码眼,外边框
                    } else if (input.getEyeOutPoint(CodeEyePositionEnum.RIGHT_TOP, inputX, inputY) == 1) {
                        if (!rightTopOut) {
                            config.getEyeOut().getShape().draw(graphics2D, CodeEyeOutDrawModel.create(config.getEyeOut().getColor(), outputX, y, multiple));
                            rightTopOut = true;
                        }
                        //左下角码眼,外边框
                    } else if (input.getEyeOutPoint(CodeEyePositionEnum.LEFT_BOTTOM, inputX, inputY) == 1) {
                        if (!leftBottomOut) {
                            config.getEyeOut().getShape().draw(graphics2D, CodeEyeOutDrawModel.create(config.getEyeOut().getColor(), outputX, y, multiple));
                            leftBottomOut = true;
                        }
                        // 左上角码眼,内边框
                    } else if (input.getEyeInsidePoint(CodeEyePositionEnum.LEFT_TOP, inputX, inputY) == 1) {
                        if (!leftTopInside) {
                            config.getEyeInside().getShape().draw(graphics2D, CodeEyeInsideDrawModel.create(config.getEyeInside().getColor(), outputX, y, multiple));
                            leftTopInside = true;
                        }
                        // 右上角码眼,内边框
                    } else if (input.getEyeInsidePoint(CodeEyePositionEnum.RIGHT_TOP, inputX, inputY) == 1) {
                        if (!rightTopInside) {
                            config.getEyeInside().getShape().draw(graphics2D, CodeEyeInsideDrawModel.create(config.getEyeInside().getColor(), outputX, y, multiple));
                            rightTopInside = true;
                        }
                        // 左下角码眼,内边框
                    } else if (input.getEyeInsidePoint(CodeEyePositionEnum.LEFT_BOTTOM, inputX, inputY) == 1) {
                        if (!leftBottomInside) {
                            config.getEyeInside().getShape().draw(graphics2D, CodeEyeInsideDrawModel.create(config.getEyeInside().getColor(), outputX, y, multiple));
                            leftBottomInside = true;
                        }
                    } else {
                        // 其他黑色区域
                        dot.getShape().draw(graphics2D, new CodeDotDrawModel(dot.getColor(), outputX, y, multiple, multiple));
                    }
                }
            }
        }
    }

    /**
     * 绘制背景图片
     *
     * @param codeImage
     * @param config
     * @param sizeInfo  尺寸信息
     * @return java.awt.image.BufferedImage
     * @author liuchao
     * @date 2023/2/22
     */
    private static BufferedImage drawBackgroundImage(BufferedImage codeImage, CodeConfig config, CodeImageSizeInfo sizeInfo) {
        CodeBackgroundConfig background = config.getBackground();
        if (ObjectUtil.isEmpty(background.getImage())) {
            return codeImage;
        }
        final int codeWidth = codeImage.getWidth();
        final int codeHeight = codeImage.getHeight();

        BufferedImage backgroundImage = ImgUtil.toBufferedImage(background.getImage());
        int imageW = backgroundImage.getWidth(null);
        int imageH = backgroundImage.getHeight(null);
        //背景图的宽高不能小于  二维码宽高
        int w = imageW < config.getWidth() ? config.getWidth() : imageW;
        int h = imageH < config.getHeight() ? config.getHeight() : imageH;
        //如果绘制X\Y都等于0,则直接填充
        if (background.getCodeDrawX() == 0 && background.getCodeDrawY() == 0) {
            w = config.getWidth();
            h = config.getHeight() + sizeInfo.getTopNoteHeight() + sizeInfo.getBottomNoteHeight();
        }

        // 背景图缩放
        if (imageW != w || imageH != h) {
            BufferedImage newBackgroundImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
            newBackgroundImage.getGraphics().drawImage(backgroundImage.getScaledInstance(w, h, Image.SCALE_SMOOTH)
                    , 0, 0, null);
            backgroundImage = newBackgroundImage;
        }

        Graphics2D graphics2D = ImageUtil.imageToGraphics2D(backgroundImage);


        int codeDrawX = (w - codeWidth) >> 1;
        int codeDrawY = (h - codeHeight) >> 1;
        //如果设置了开始绘制的坐标,则按照这个坐标绘制
        if (background.getCodeDrawX() != 0 || background.getCodeDrawY() != 0) {
            codeDrawX = background.getCodeDrawX();
            codeDrawY = background.getCodeDrawY();
        }

        graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1F));
        graphics2D.drawImage(codeImage.getScaledInstance(codeWidth, codeHeight, Image.SCALE_SMOOTH), codeDrawX, codeDrawY,
                null);
        graphics2D.dispose();
        backgroundImage.flush();
        return backgroundImage;
    }

    /**
     * 绘制背景图片
     *
     * @param graphics2D
     * @param sizeInfo   尺寸信息
     * @param config     配置信息
     * @return void
     * @author liuchao
     * @date 2023/2/21
     */
    private static void drawBackgroundColor(Graphics2D graphics2D, CodeImageSizeInfo sizeInfo, CodeConfig config, BitMatrix matrix) {
        CodeBackgroundConfig background = config.getBackground();
        //如果设置了背景图片,则忽略背景色
        if (ObjectUtil.isNotEmpty(background.getImage())) {
            return;
        }
        //设置二维码区域背景颜色  全部为设置的背景色
        graphics2D.setColor(background.getColor());
        graphics2D.fillRect(0, sizeInfo.getTopNoteHeight(), matrix.getWidth(), matrix.getHeight());
    }


    /**
     * 转换二维码最终图片尺寸信息
     *
     * @param config 二维码配置信息
     * @return com.faea.qrcode.util.CodeUtil.CodeImageSizeInfo
     * @author liuchao
     * @date 2023/2/18
     */
    private static CodeImageSizeInfo convertCodeImageSizeInfo(CodeConfig config) {
        int qrCodeWidth = config.getWidth();
        int qrCodeHeight = config.getHeight();
        CodeImageSizeInfo info = new CodeImageSizeInfo();
        //二维码上注释
        Integer topNoteHeight = 0;
        CodeNoteConfig topNote = config.getTopNote();
        if (null != topNote) {
            FontMetrics topNoteFont = convertFont(topNote);
            int topNoteRowHeight = topNoteFont.getHeight();
            int topNoteWidth = topNoteFont.stringWidth(topNote.getContent());
            if (topNoteWidth > qrCodeWidth) {
                int topNoteRowSize = topNoteWidth / qrCodeWidth;
                if (topNoteWidth % qrCodeWidth != 0) {
                    topNoteRowSize += 1;
                }
                info.setTopNoteRowSize(topNoteRowSize);
                topNoteHeight += (topNote.getRowSpacing() + topNoteRowHeight) * topNoteRowSize;
            } else {
                topNoteHeight += (topNote.getRowSpacing() + topNoteRowHeight);
            }
            info.setTopNoteHeight(topNoteHeight);
            info.setTopNoteFontMetrics(topNoteFont);
        }

        CodeNoteConfig bottomNote = config.getBottomNote();
        Integer bottomNoteHeight = 0;
        if (null != bottomNote) {
            //二维码下注释
            FontMetrics bottomNoteFont = convertFont(bottomNote);
            int bottomNoteRowHeight = bottomNoteFont.getHeight();
            int bottomNoteWidth = bottomNoteFont.stringWidth(bottomNote.getContent());
            if (bottomNoteWidth > qrCodeWidth) {
                int bottomNoteRowSize = bottomNoteWidth / qrCodeWidth;
                if (bottomNoteWidth % qrCodeWidth != 0) {
                    bottomNoteRowSize += 1;
                }
                info.setBottomNoteRowSize(bottomNoteRowSize);
                bottomNoteHeight += (bottomNote.getRowSpacing() + bottomNoteRowHeight) * bottomNoteRowSize;
            } else {
                bottomNoteHeight += (bottomNote.getRowSpacing() + bottomNoteRowHeight);
            }
            info.setBottomNoteHeight(bottomNoteHeight);
            info.setBottomNoteFontMetrics(bottomNoteFont);
        }
        info.setCodeImageTotalHeight(topNoteHeight + qrCodeHeight + bottomNoteHeight);
        return info;
    }

    /**
     * 二维码尺寸信息
     *
     * @author liuchao
     * @date 2023/2/18
     */
    @Data
    static class CodeImageSizeInfo {
        /**
         * 顶部注释占用高度
         */
        int topNoteHeight;


        /**
         * 顶部注释 字体信息
         */
        FontMetrics topNoteFontMetrics;

        /**
         * 顶部注释占用行数
         */
        int topNoteRowSize;

        /**
         * 底部注释占用高度
         */
        int bottomNoteHeight;

        /**
         * 底部注释占用行数
         */
        int bottomNoteRowSize;

        /**
         * 底部注释 字体信息
         */
        FontMetrics bottomNoteFontMetrics;

        /**
         * 二维码图片总高度
         */
        int codeImageTotalHeight;


    }

}

4、效果查看

4.1、效果单元测试

总体写了14个单元测试,通过单元测试可以看出,使用起来还是很方便的。

package com.faea;

import com.faea.qrcode.config.CodeConfig;
import com.faea.qrcode.config.CodeLogoConfig;
import com.faea.qrcode.config.CodeNoteConfig;
import com.faea.qrcode.constants.CodeDotShapeEnum;
import com.faea.qrcode.constants.CodeEyeInsideShapeEnum;
import com.faea.qrcode.constants.CodeEyeOutShapeEnum;
import com.faea.qrcode.constants.CodeLogoShapeEnum;
import com.faea.qrcode.util.CodeUtil;
import org.junit.Test;

import java.awt.*;
import java.io.File;

/**
 * 二维码测试
 *
 * @author liuchao
 * @date 2023/2/23
 */
public class CodeTest {

    /**
     * 保存二维码地址
     */
    private static final String SAVE_FILE_PATH = "src/test/code/";

    /**
     * 二维码内容
     */
    private static final String content = "https://blog.csdn.net/u011837804";

    /**
     * 注释
     */
    private static final String note = "Java全栈行动派";

    /**
     * 普通二维码
     */
    @Test
    public void test1() {
        CodeUtil.generate(content, 300, 300,
                new File(SAVE_FILE_PATH + "gen/test1.png"));
    }

    /**
     * 带logo
     */
    @Test
    public void test2() {
        CodeUtil.generate(content, 300, 300,
                ImgUtil.read(new File(SAVE_FILE_PATH + "logo.png")),
                new File(SAVE_FILE_PATH + "gen/test2.png"));
    }

    /**
     * 圆形logo
     */
    @Test
    public void test3() {
        CodeConfig config = new CodeConfig(300, 300)
                .logo(new CodeLogoConfig(ImgUtil.read(new File(SAVE_FILE_PATH + "logo.png"))).shape(CodeLogoShapeEnum.CIRCLE));

        CodeUtil.generate(content, config,
                new File(SAVE_FILE_PATH + "gen/test3.png"));
    }

    /**
     * 原型logo
     */
    @Test
    public void test4() {
        CodeConfig config = new CodeConfig(300, 300)
                .logo(new CodeLogoConfig(ImgUtil.read(new File(SAVE_FILE_PATH + "logo.png"))).shape(CodeLogoShapeEnum.ORIGINAL));

        CodeUtil.generate(content, config,
                new File(SAVE_FILE_PATH + "gen/test4.png"));
    }

    /**
     * 圆角二维码
     */
    @Test
    public void test5() {
        CodeConfig config = new CodeConfig(300, 300)
                .logo(new CodeLogoConfig(ImgUtil.read(new File(SAVE_FILE_PATH + "logo.png"))))
                .arc(0.3D);
        CodeUtil.generate(content, config,
                new File(SAVE_FILE_PATH + "gen/test5.png"));
    }

    /**
     * 背景颜色
     */
    @Test
    public void test6() {
        CodeConfig config = new CodeConfig(300, 300)
                .logo(new CodeLogoConfig(ImgUtil.read(new File(SAVE_FILE_PATH + "logo.png"))));
        config.getBackground().color(ImgUtil.getColor(129590));
        CodeUtil.generate(content, config, new File(SAVE_FILE_PATH + "gen/test6.png"));
    }

    /**
     * 设置背景图片
     */
    @Test
    public void test7() {
        CodeConfig config = new CodeConfig(300, 300)
                .logo(new CodeLogoConfig(ImgUtil.read(new File(SAVE_FILE_PATH + "logo.png"))));
        config.getBackground().image(ImgUtil.read(new File(SAVE_FILE_PATH + "background.png")));
        CodeUtil.generate(content, config, new File(SAVE_FILE_PATH + "gen/test7.png"));
    }

    /**
     * 设置上注释
     */
    @Test
    public void test8() {
        CodeConfig config = new CodeConfig(300, 300)
                .logo(new CodeLogoConfig(ImgUtil.read(new File(SAVE_FILE_PATH + "logo.png"))));
        config.topNote(new CodeNoteConfig().content(note).backColor(Color.WHITE));
        CodeUtil.generate(content, config, new File(SAVE_FILE_PATH + "gen/test8.png"));
    }

    /**
     * 设置上注释 + 背景颜色+ 字颜色
     */
    @Test
    public void test9() {
        CodeConfig config = new CodeConfig(300, 300)
                .logo(new CodeLogoConfig(ImgUtil.read(new File(SAVE_FILE_PATH + "logo.png"))));
        config.topNote(new CodeNoteConfig().content(note).backColor(Color.WHITE));
        CodeUtil.generate(content, config, new File(SAVE_FILE_PATH + "gen/test9.png"));
    }

    /**
     * 设置下注释
     */
    @Test
    public void test10() {
        CodeConfig config = new CodeConfig(300, 300)
                .logo(new CodeLogoConfig(ImgUtil.read(new File(SAVE_FILE_PATH + "logo.png"))));
        config.bottomNote(new CodeNoteConfig().content(note).backColor(Color.WHITE));
        CodeUtil.generate(content, config, new File(SAVE_FILE_PATH + "gen/test10.png"));
    }

    /**
     * 设置码点颜色
     */
    @Test
    public void test11() {
        CodeConfig config = new CodeConfig(300, 300)
                .logo(new CodeLogoConfig(ImgUtil.read(new File(SAVE_FILE_PATH + "logo.png"))));
        config.bottomNote(new CodeNoteConfig().content(note).backColor(Color.WHITE).fontColor(Color.BLUE));
        config.getDot().color(Color.BLUE);
        CodeUtil.generate(content, config, new File(SAVE_FILE_PATH + "gen/test11.png"));
    }

    /**
     * 设置码眼颜色
     */
    @Test
    public void test12() {
        CodeConfig config = new CodeConfig(300, 300)
                .logo(new CodeLogoConfig(ImgUtil.read(new File(SAVE_FILE_PATH + "logo.png"))));
        config.bottomNote(new CodeNoteConfig().content(note).backColor(Color.WHITE).fontColor(Color.BLUE));
        config.getDot().color(Color.BLUE);
        config.getEyeInside().color(Color.BLUE);
        config.getEyeOut().color(Color.RED);
        CodeUtil.generate(content, config, new File(SAVE_FILE_PATH + "gen/test12.png"));
    }

    /**
     * 设置码点形状
     */
    @Test
    public void test13() {
        CodeConfig config = new CodeConfig(300, 300)
                .logo(new CodeLogoConfig(ImgUtil.read(new File(SAVE_FILE_PATH + "logo.png"))));
        config.bottomNote(new CodeNoteConfig().content(note).backColor(Color.WHITE).fontColor(Color.BLUE));
        config.getDot().color(Color.BLUE).shape(CodeDotShapeEnum.DIAMOND);
        config.getEyeInside().color(Color.BLUE);
        config.getEyeOut().color(Color.RED);
        CodeUtil.generate(content, config, new File(SAVE_FILE_PATH + "gen/test13.png"));
    }

    /**
     * 设置码眼形状
     */
    @Test
    public void test14() {
        CodeConfig config = new CodeConfig(300, 300)
                .logo(new CodeLogoConfig(ImgUtil.read(new File(SAVE_FILE_PATH + "logo.png"))));
        config.bottomNote(new CodeNoteConfig().content(note).backColor(Color.WHITE).fontColor(Color.BLUE));
        config.getDot().color(Color.BLUE).shape(CodeDotShapeEnum.TRIANGLE);
        config.getEyeInside().color(Color.BLUE).shape(CodeEyeInsideShapeEnum.ROUNDED);
        config.getEyeOut().color(Color.RED).shape(CodeEyeOutShapeEnum.ROUNDED_THICK_EDGE);
        CodeUtil.generate(content, config, new File(SAVE_FILE_PATH + "gen/test14.png"));
    }
}

4.2、生成所有的效果二维码

由于文章审核要求,所以做了图片模糊,但是二维码优化效果还是可以看清楚的。

不可否认的是要实现这些功能,需要对二维码生成原理、zxing源码等非常属性,着实让小编下了很大的功能,从上面小编贴出来的代码可以看出针对每个设置项注释是非常到位的,核心工具类也贴出来了,希望对朋友们的开发有帮助,全部源码下载地址:https://download.csdn.net/download/u011837804/87490526

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

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

相关文章

STC单片机启动看门狗定时器介绍和使用

STC单片机启动看门狗定时器介绍 ✨这里以STC8系列为例。 📑看门狗复位(WDT_CONTR) WDT_FLAG:看门狗溢出标志 看门狗发生溢出时,硬件自动将此位置 1,需要软件清零。EN_WDT:看门狗使能位 0:对单片机无影响 1:启动看门狗定时器。 注意:看门狗定时器可使用软件方式启动,…

JSP网上书店系统用myeclipse定制开发mysql数据库B/S模式java编程计算机网页

一、源码特点 JSP 网上书店系统 是一套完善的系统源码&#xff0c;对理解JSP java 编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。研究的基本内容是基于网上书店系 统&#xff0c;使用JSP作为页面开发工具。Web服务的运…

【机器学习】集成学习投票法:投票回归器(VotingRegressor) 投票分类器(VotingClassifier)

前言 投票回归器和投票分类器都属于集成学习。在【机器学习】集成学习基础概念介绍中有提到过&#xff0c;集成学习的结合策略包括&#xff1a; 平均法、投票法和学习法。sklearn.ensemble库中的Voting Classifier和Voting Regressor&#xff0c;它们分别实现了对回归任务和分…

JUC 之 Synchronized 与 锁升级

—— 对象内存布局 和 对象头 对象构成布局 1. 对象头 对象标记 Mark Word 哈希码GC 标记 & 次数 GC 年龄 采用 4 位 bit 存储&#xff0c;最大为 15&#xff08;1111&#xff09;&#xff0c;所以 MaxTenutingThreshold 参数&#xff08;分代年龄&#xff09;的参数默…

C++回顾(三)—— 函数

3.1 内联函数 3.1.1 内联函数的定义 &#xff08;1&#xff09;内联函数的作用 作用&#xff1a;不是在调用时发生控制转移&#xff0c;而是在编译时将函数体嵌入在每一个调用处&#xff0c;适用于功能简单&#xff0c;规模较小又使用频繁的函数。递归函数无法内联处理&…

Java-重排序,happens-before 和 as-if-serial 语义

目录1. 如何解决重排序带来的问题2. happens-before1. 如何解决重排序带来的问题 对于编译器&#xff0c;JMM 的编译器重排序规则会禁止特定类型的编译器重排序。对于处理器重排序&#xff0c;JMM 的处理器重排序规则会要求编译器在生成指令序列时&#xff0c;插入特定类型的内…

Android笔记(二十五):两种sdk热更插件资源加载方案

背景 在研究sdk插件化热更新方式的过程中总结出了两套插件资源加载方案&#xff0c;在此记录下 资源热更方式 方式一&#xff1a;合并所有插件资源 需要解决资源id冲突问题 资源ID值一共4个字段&#xff0c;由三部分组成&#xff1a;PackageIdTypeIdEntryId PackageId&…

Mysql 事务的隔离性(隔离级别)

Mysql 中的事务分为手动提交和自动提交&#xff0c;默认是自动提交&#xff0c;所以我们在Mysql每输入一条语句&#xff0c;其实就会被封装成一个事务提交给Mysql服务端。 手动提交需要先输入begin&#xff0c;表示要开始处理事务&#xff0c;然后就是常见的sql语句操作了&…

C++之入门之命名空间、缺省参数、函数重载

一、前言 我们知道c是对c语言的完善以及再发展&#xff0c;所以C中的很多东西是与C语言十分修饰的&#xff0c;并且C也是兼容C的&#xff0c;学习了C之后&#xff0c;相信学C也不在困难&#xff0c;对我们来说&#xff0c;唯一感到不解和陌生就只有 using namespace std; 这条…

【c++】STL1—STL初识

文章目录STL的基本概念STL六大组件STL中容器、算法、迭代器容器算法迭代器容器算法迭代器初识vector存放内置数据类型vector存放自定义数据类型容器嵌套容器c的面向对象和泛型编程思想&#xff0c;目的就是复用性的提升。 为了建立数据结构和算法的一套标准&#xff0c;诞生了S…

并查集(13张图解)--擒贼先擒王

目录 前言 故事 &#x1f33c;思路 &#x1f33c;总结 &#x1f33c;代码 &#x1f44a;观察过程代码 &#x1f44a;正确代码 &#x1f44a;细节代码 来自《啊哈算法》 前言 刚学了树在优先队列中的应用--堆的实现 那么树还有哪些神奇的用法呢&#xff1f;我们从一…

前端卷算法系列(二)

前端卷算法系列&#xff08;二&#xff09; 回文数 给你一个整数 x &#xff0c;如果 x 是一个回文整数&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 回文数是指正序&#xff08;从左向右&#xff09;和倒序&#xff08;从右向左&#xff09;读都是一样…

zookeeper集群的搭建,菜鸟升级大神必看

一、下载安装zookeeperhttp://archive.apache.org/dist/zookeeper/下载最新版本2.8.1http://archive.apache.org/dist/zookeeper/zookeeper-3.8.1/二、上传安装包到服务器上并且解压&#xff0c;重命名tar -zxvf apache-zookeeper-3.8.1-bin.tar.gzmv apache-zookeeper-3.8.1-b…

设计环形队列

文章目录1.思路分析1.1队列空满分析1.2出队分析2.循环队列设计1.思路分析 1.1队列空满分析 首先我们假设一个长度为4的环形队列 队头front 队尾rear 当队列为空时 frontrear 当队列满时 frontrear 所以我们无法判断队列是满的或者空的 因此我们多加入一个空间使队列长度为5&am…

什么是自适应平台服务?

总目录链接==>> AutoSAR入门和实战系列总目录 文章目录 什么是自适应平台服务?1.1 自适应平台服务包含哪些功能簇呢?1.1.1 ara::sm 状态管理 (SM)1.1.2 ara::diag 诊断管理 (DM)1.1.3 ara::s2s 信号到服务映射1.1.4 ara::nm 网络管理 (NM)1.1.5 ara::ucm 更新和配置管…

数据结构期末复习总结(前章)

作者的话 作为一名计算机类的学生&#xff0c;我深知数据结构的重要性。在期末复习前&#xff0c;我希望通过这篇博客给大家一些复习建议。希望能帮助大家夯实数据结构的基础知识&#xff0c;并能够更好地掌握数据结构和算法的应用。 一、绪论 数据&#xff1a;信息的载体&am…

【测试】loadrunner安装

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录备注一、下载安装包二、安装loadrunner三、修改浏览器配置今天搬砖不努力&#xff0c;明天地位不稳定&#xff01; 备注 电脑最好有IE浏览器&#xff0c;但是没有也没事儿。&#xff08;注意&#xff1a;IE浏览器不…

Bootstrap系列之栅格系统

Bootstrap栅格系统 bootatrap提供了一套响应式&#xff0c;移动设备优先的流式网格系统&#xff0c;随着屏幕或者视口尺寸的增加&#xff0c;系统会自动分为最多12列&#xff0c;多出12列的将不再此行显示&#xff08;换行显示&#xff09; bootstrap网格系统有以下六个类 重点…

华为OD机试用Python实现 -【云短信平台优惠活动】(2023-Q1 新题)

华为OD机试题 华为OD机试300题大纲云短信平台优惠活动题目描述输入描述输出描述示例一输入输出说明示例二输入输出说明Python 代码实现代码编写思路华为OD机试300题大纲 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看…

【Java基础】操作系统原理

一、进程 进程是指一段程序的执行过程&#xff0c;会消耗系统资源如CPU、内存、网络等。 一个进程包含静态代码段&#xff0c;数据&#xff0c;寄存器地址等 进程的特点 动态性&#xff08;可动态地创建、结束进程&#xff09; 并发性&#xff08;进程被独立调度并占用处理…