Java生成微信分享海报【基础设计】

2020-02-23 16:03:23来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

Java生成微信分享海报【基础设计】

前言

微信后台生成海报一般都是一个模板写死,然后就完事了,过了不久让修改个模板,就又要看半天,还要考虑是否重新复制一份改一改,越来越多的重复代码,全在一个图片类里,然后就越来越乱。这两天用设计模式处理了一下,让以后修改模板,新增模板更舒服一点。有第三方好用的轻量级的实现,还请留言。感激!!

起步

  • 了解IO
  • 了解awt
  • 装饰者设计模式

开始

  • demo地址

喜欢直接看项目的可以直接 >> demo-common

  • 目录结构

目录结构

抽象层(abst目录)

  • 海报抽象类
/**
 * 海报抽象类
 * @author quaint
 * @date 21 February 2020
 * @since master
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class AbstractPoster implements Poster {


    /**
     * 背景图
     */
    protected BufferedImage backgroundImage;

    /**
     * logo
     */
    protected BufferedImage logo;

    /**
     * 广告语
     */
    protected String slogan;

    /**
     * 主图
     */
    protected BufferedImage mainImage;

    /**
     * 二维码
     */
    protected BufferedImage qrcode;


}
  • 海报装饰抽象类
/**
 * 海报装饰抽象类
 * @author quaint
 * @date 21 February 2020
 * @since master
 */
@Data
@AllArgsConstructor
public abstract class AbstractPosterDecorator implements Poster {

    protected Poster poster;

    protected int positionX;

    protected int positionY;

    protected int width;

    protected int height;

    public AbstractPosterDecorator(Poster poster){
        this.poster = poster;
    }

    @Override
    public BufferedImage draw(BufferedImage image) {
        System.out.println("默认绘制方法");
        return poster.draw(image);
    }
}
  • 海报接口定义
/**
 * 海报接口定义
 * @author quaint
 * @date 21 February 2020
 * @since master
 */
public interface Poster {

    /**
     * 画海报
     * @param image image
     * @return image
     */
    BufferedImage draw(BufferedImage image);

}
  • 海报绘制接口定义
/**
 * 海报绘制接口定义
 * @author quaint
 * @date 21 February 2020
 * @since master
 */
public interface PosterDraw<T>{


    /**
     * 是否支持
     * @param clazz class
     * @return bool
     */
    boolean support(Class<?> clazz);

    /**
     * 通过数据绘制 并返回 图片
     * @param data 海报所需的数据
     * @return 图片
     * @throws IOException io
     */
    BufferedImage drawAndReturnImage(T data) throws IOException;

}

具体海报特有属性(content目录)

/**
 * 朋友小卡片海报
 * @author quaint
 * @date 21 February 2020
 * @since master
 */
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
public class MiniAppCardPoster extends AbstractPoster {

    /**
     * 用户头像
     */
    private BufferedImage headImage;

    /**
     * 用户昵称
     */
    private String userNickName;

    /**
     * 价格范围
     */
    private String priceRange;

    /**
     * 划线价
     */
    private String linePrice;


    @Builder(toBuilder = true)
    public MiniAppCardPoster(BufferedImage backgroundImage, BufferedImage logo, String slogan, BufferedImage mainImage, BufferedImage qrcode, BufferedImage headImage, String userNickName, String priceRange, String linePrice) {
        super(backgroundImage, logo, slogan, mainImage, qrcode);
        this.headImage = headImage;
        this.userNickName = userNickName;
        this.linePrice = linePrice;
        this.priceRange = priceRange;
    }

    @Override
    public BufferedImage draw(BufferedImage image) {
        return image;
    }

}

装饰者实现代码(decorators目录)

  • 背景装饰
/**
 * 背景装饰
 * @author quaint
 * @date 21 February 2020
 * @since master
 */
public class BackgroundDecorator extends AbstractPosterDecorator {

    public BackgroundDecorator(Poster poster) {
        super(poster);
    }

    @Builder(toBuilder = true)
    public BackgroundDecorator(Poster poster, int positionX, int positionY, int width, int height) {
        super(poster,positionX,positionY,width,height);
    }


    @Override
    public BufferedImage draw(BufferedImage image) {
        // 绘制 被装饰之前的 图片
        BufferedImage draw = poster.draw(image);
        // 装饰, 绘制头像
        return drawBackground(draw);
    }

    /**
     * 绘制背景具体实现
     * @param image image
     * @return image
     */
    private BufferedImage drawBackground(BufferedImage image){

        // 如果宽度没变化
        if (width == image.getWidth() && height == image.getHeight()){
            return image;
        }
        // 调整背景宽度
        if (width!=0 && height !=0){
            BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
            Graphics2D g = newImage.createGraphics();
            g.drawImage(image,0,0,width,height,null);
            g.dispose();
            return newImage;
        }
        // 绘制背景
        return image;
    }
}
  • 图片装饰
/**
 * 绘制 图片
 * @author quaint
 * @date 21 February 2020
 * @since master
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class ImageDecorator extends AbstractPosterDecorator {

    /**
     * 要绘制的图片
     */
    private BufferedImage image;

    /**
     * 是否修改为圆形
     */
    private boolean circle;

    public ImageDecorator(Poster poster) {
        super(poster);
    }

    @Builder(toBuilder = true)
    public ImageDecorator(Poster poster, int positionX, int positionY, int width, int height, BufferedImage image, boolean circle) {
        super(poster,positionX,positionY,width,height);
        this.image = image;
        this.circle = circle;
    }

    @Override
    public BufferedImage draw(BufferedImage image) {
        // 绘制 被装饰之前的 图片
        BufferedImage draw = poster.draw(image);
        // 装饰, 绘制头像
        return drawImage(draw);
    }

    /**
     * 绘制图片具体实现
     * @param sourceImage sourceImage
     * @return image
     */
    private BufferedImage drawImage(BufferedImage sourceImage){

        if (image == null){
            return sourceImage;
        }

        // 实现绘制图片
        Graphics2D g = sourceImage.createGraphics();

        if (circle){
            // 设置形状
            Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, image.getWidth(), image.getHeight());

            BufferedImage output = new BufferedImage(image.getWidth(),image.getHeight(),BufferedImage.TYPE_4BYTE_ABGR);
            Graphics2D g2 = output.createGraphics();
            output = g2.getDeviceConfiguration().createCompatibleImage(image.getWidth(), image.getHeight(), Transparency.TRANSLUCENT);
            g2 = output.createGraphics();

            // 将背景设置为透明。如果注释该段代码,默认背景为白色.也可通过g2.setPaint(paint) 设置背景色
            g2.setComposite(AlphaComposite.Clear);
            g2.fill(new Rectangle(image.getWidth(), image.getHeight()));
            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1));
            g2.setClip(shape);
            // 使用 setRenderingHint 设置抗锯齿
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.drawImage(image, 0, 0, null);
            g2.dispose();
            image = output;

        }
        g.drawImage(image, positionX, positionY, width, height, null);
        g.dispose();

        return sourceImage;
    }

}
  • 文本装饰
/**
 * 绘制文本
 * @author quaint
 * @date 21 February 2020
 * @since master
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class TextDecorator extends AbstractPosterDecorator {

    /**
     * 字体
     */
    private Font font = new Font(null);

    /**
     * 字体样式
     */
    private int fontStyle = Font.PLAIN;

    /**
     * 字体大小
     */
    private int fontSize = 16;

    /**
     * 字体颜色
     */
    private Color color = new Color(255,255,255);

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

    /**
     * 是否包含删除先
     */
    private boolean delLine = false;


    public TextDecorator(Poster poster) {
        super(poster);
    }

    @Builder(toBuilder = true)
    public TextDecorator(Poster poster, int positionX, int positionY, int width, int height, Font font, int fontSize, Color color, String content, int fontStyle, boolean delLine) {
        super(poster,positionX,positionY,width,height);
        this.font = font;
        this.fontSize = fontSize;
        this.color = color;
        this.content = content;
        this.fontStyle = fontStyle;
        this.delLine = delLine;
    }

    @Override
    public BufferedImage draw(BufferedImage image) {
        // 绘制 被装饰之前的 图片
        BufferedImage draw = poster.draw(image);
        // 装饰, 绘制文本
        return drawText(draw);
    }

    /**
     * 绘制文本具体实现
     * @param image image
     * @return image
     */
    private BufferedImage drawText(BufferedImage image){

        if (StringUtils.isEmpty(content)){
            return image;
        }

        // 实现绘制文本
        font = font.deriveFont(fontStyle, fontSize);
        Graphics2D g = image.createGraphics();
        // 设置文字抗锯齿算法
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING , RenderingHints.VALUE_ANTIALIAS_ON);
        g.setFont(font);
        g.setColor(color);
        g.drawString(content, positionX, positionY+fontSize);
        if (delLine){
            // 计算非汉字长度
            int shortNum = content.replaceAll("[^0-9,a-z,A-Z,.]", "").length();
            // 汉字长度
            int longNum = content.length()-shortNum;
            // 删除线长度 = (汉字长度 * size) + ((字符长度+1) * size/2)
            int num = longNum + (shortNum+1)/2;
            g.drawLine(positionX-fontSize/3,positionY+3*fontSize/5,positionX+fontSize*num,positionY+3*fontSize/5);
        }
        g.dispose();
        return image;
    }

}

绘制具体类型海报(draw目录)

/**
 * @author quaint
 * @date 21 February 2020
 * @since master
 */
@Component
@Slf4j
public class MiniAppCardDraw implements PosterDraw<MiniAppCardPoster> {


    @Override
    public boolean support(Class<?> clazz) {
        if (clazz == null){
            return false;
        }
        return clazz.equals(MiniAppCardPoster.class);
    }

    @Override
    public BufferedImage drawAndReturnImage(MiniAppCardPoster poster) throws IOException{
        log.info("[drawAndReturnImage] method start, param:[{}]", poster);
        // ======= 主逻辑开始 --> =======

        // 1. 绘制背景 最好取背景的 width 和 height
        BackgroundDecorator drawBg = new BackgroundDecorator(poster).toBuilder()
                .width(420).height(336).build();
        // 2. 绘制头像
        ImageDecorator drawHead = new ImageDecorator(drawBg).toBuilder()
                .positionX(27).positionY(27)
                .width(36).height(36)
                .circle(true)
                .image(poster.getHeadImage()).build();
        // 3. 绘制昵称
        TextDecorator drawNickName = new TextDecorator(drawHead).toBuilder()
                .positionX(71).positionY(32)
                .fontSize(18)
                .content(poster.getUserNickName()+" 向你推荐").build();
        // 3. 绘制商品介绍
        TextDecorator drawSlogan = new TextDecorator(drawNickName).toBuilder()
                .positionX(27).positionY(70)
                .fontSize(22).fontStyle(Font.BOLD)
                .content(poster.getSlogan()).build();
        // 4. 绘制商品图片
        ImageDecorator drawProdImg = new ImageDecorator(drawSlogan).toBuilder()
                .positionX(24).positionY(129)
                .width(168).height(168)
                .image(poster.getMainImage()).build();
        // 5. 绘制价格返回
        TextDecorator drawPriceRange = new TextDecorator(drawProdImg).toBuilder()
                .positionX(203).positionY(155)
                .fontSize(24).fontStyle(Font.BOLD)
                .color(new Color(216,11,42))
                .content(poster.getPriceRange()).build();
        // 6. 绘制删除线价格
        TextDecorator drawLinePrice = new TextDecorator(drawPriceRange).toBuilder()
                .positionX(240).positionY(187)
                .fontSize(18).delLine(true)
                .color(new Color(153,153,153))
                .content(poster.getLinePrice()).build();
        // 调用最后一个包装类的 draw 方法
        BufferedImage drawResult = drawLinePrice.draw(poster.getBackgroundImage());

        // ======= <-- 主逻辑结束 =======
        log.info("[drawAndReturnImage] method end, result:[success]");
        return drawResult;
    }
}

效果图

效果图


原文链接:https://www.cnblogs.com/quaint/p/12349817.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇: 高并发之——不得不说的线程池与ThreadPoolExecutor类浅析

下一篇:池化技术——自定义线程池