1. 程式人生 > >Java生成微信分享海報【基礎設計】

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;
    }
}

效果圖

相關推薦

Java生成分享海報基礎設計

前言 微信後臺生成海報一般都是一個模板寫死,然後就完事了,過了不久讓修改個模板,就又要看半天,還要考慮是否重新複製一份改一改,越來越多的重複程式碼,全在一個圖片類裡,然後就越來越亂。這兩天用設計模式處理了一下,讓以後修改模板,新增模板更舒服一點。有第三方好用的輕量級的實現,還請留言。感激!! 起步 瞭解IO

java呼叫分享

這篇文章主要是介紹如何使用java開發微信分享功能,因為工作,已經開發完成,可使用。下面有聯絡方式,可交流 如果想要自定義微信的分享功能,首先在自己的頁面內首先使用AJAX。下面我具體舉例。 首先是在頁面內寫入請求後臺的AJAX /** *

Java 生成群頭像 九宮格頭像

通過獲取使用者網路路徑地址的 頭像.生成一張網路路徑的九宮格頭像. dir是網路伺服器路徑import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Font; import java.awt

介面開發php程式碼

首先準備一個api.php檔案 這個檔案可以再微信開發平臺網站手冊裡面下載到 需要配置的是token 微信測試地址 https://mp.weixin.qq.com/debug/ <?php /** * wechat php test */ //define yo

關注公眾號OpenCV學堂 -應知空谷幽人意,只在春風淡蕩中

獨立影象處理開發者 影象處理與物件識別演算法外包 OCR與美化類濾鏡開發 安卓與IOS影象類應用開發 OpenCV學習群:376281510 學習影象處理技術關注 公眾號:【OpenCV學堂】 個人聯絡方式: QQ: 57558865

關注公眾號OpenCV學堂( -應知空谷幽人意,只在春風淡蕩中)

獨立影象處理開發者 影象處理與物件識別演算法外包 OCR與美化類濾鏡開發 安卓與IOS影象類應用開發 OpenCV學習群:376281510 學習影象處理技術關注 公眾號:【OpenCV學堂】 個人聯絡方式: QQ: 57558865

關注公眾號OpenCV學堂

影象處理之基於閾值模糊 演算法思想: 實現一個高斯卷積模糊但是隻運用與周圍的畫素值與中心畫素值差值小於閾值。兩個 畫素值之間的距離計算可以選用向量距離即曼哈頓距離或者歐幾里德距離。高斯模糊 採用先XY方向一維高斯模糊完成目的是為了減小計算量。 程式效果: 關鍵程式碼解釋:

java菜鳥之分享

簡單 .net 小寫 logging cgi acc creat -type 以及 前言:我終於理解了什麽叫做教科書:教科書就是把一些簡單容易的知識寫成一堆誰都看不懂的書,這,就簡稱“教科書” 這些天接觸到微信分享以及回調的問題,因為之前沒接觸過,所以這次做這個分享,碰了一

Java搭建公眾號(二)生成access_token

當自己的程式需要訪問微信的HTTP介面時,需要傳遞access_token作為校驗的引數。access_token需要通過APPID和APPSecret祕鑰來生成,有效期是7200秒,2小時。access_token最好是做成全域性變數共享,然後由一個執行緒定時去重新整理,這樣可以減少access_

Java仿全棧 高效能後臺+移動客戶端分享

第1章 課程介紹 課程介紹 第2章 netty介紹與相關基礎知識 初識netty,學習阻塞與非阻塞,同步與非同步,理解BIO、NIO、AIO以及netty的執行緒模型 第3章 使用netty編寫第一個hello netty 伺服器 從零開始使用netty編寫伺服器,在網頁訪問後返回hello

某課最全Java仿全棧 高效能後臺+移動客戶端分享

第1章 課程介紹 課程介紹 第2章 netty介紹與相關基礎知識 初識netty,學習阻塞與非阻塞,同步與非同步,理解BIO、NIO、AIO以及netty的執行緒模型 第3章 使用netty編寫第一個hello netty 伺服器 從零開始使用netty編寫伺服器,在

java-分享自定義內容功能(總結)

               java寫微信分享自定義內容功能遇到的一個bug    需求:使用者通過關注公眾號  開啟邀請好友頁面(前提必須先登入)  點選手機右上角豎形

分享最完整某課Java仿全棧 高效能後臺+移動客戶端

第1章 課程介紹 課程介紹 第2章 netty介紹與相關基礎知識 初識netty,學習阻塞與非阻塞,同步與非同步,理解BIO、NIO、AIO以及netty的執行緒模型 第3章 使用netty編寫第一個hello netty 伺服器 從零開始使用netty編寫伺服器,在網頁

java後臺獲取分享二維碼 並返回給前端

最近公司業務需求 微信小程式需要分享 從後臺請求生成二維碼圖片 並顯示 給大家分享一下 經驗 第一步 :獲得 微信token(這個token兩個小時會過期 所以需要token生成相對應的二維碼) String wxspAppid = "yourAppid";

Python3.6生成好友個性簽名詞雲

程式碼連結:https://gitee.com/AI-Echo/codes/4bk0tsqczpe6dfyhu7mlo12 思路: 1.通過itchat爬取微信好友資訊,獲取好友簽名Signature 2.將Signature拼接成Str,並去掉預先設定的一些停用詞(如的、是、有等

Java網頁授權獲取使用者資訊 分享

1. 微信授權獲取使用者資訊 1. 網頁連結 javascript: var redirectUrl = window.location.protocol + "//" + window.location.host + "/web/share/index.h

Java實現朋友圈分享

pict tst 純java googl 分享圖片 gas desc 地址 section 純Java實現微信朋友圈分享圖 1.實現分享圖的效果 2.開發環境 2.1 JDK * oracle‘s jdk 1.8以上 2.2 字體 * 若選擇了微軟雅黑字體又是代碼部署到L

Java支付功能分享

微信支付越來越成為電商們熱衷的支付方式,微信官方也給分享了微信支付的原始碼,但缺少詳細的指引,對於新手來說會浪費大量的時間。 本場 Chat 主要適用於有微信支付需求但未進行過微信支付開發的開發者們,以案例的形式詳細的講述微信支付的整個流程,參與活動我會給大夥分享可以跑通 Java 原始碼,只需修

Java支付總結(一):獲得prepay_id(以及生成支付簽名)

預設你已經取得了微信支付相關的許可權,並且有了商戶號,key值等等。 如果你一直簽名錯誤,請看上一篇帖子:http://blog.csdn.net/qq_25821067/article/details/55253399 要想獲取prepay_id,就必須要生成正確的簽名,

轉載分享發生TransactionTooLargeException 異常記錄

原博地址: https://blog.csdn.net/xiaolli/article/details/60577331 博主@程立001 前提條件: 1. 開啟雙應用(設定-》雙應用) 2. 安裝微信。 復現步驟: 1. 用相機拍一張6M左右的照片