swing-创建一个类似Carbon的代码转图片的小工具

Carbon 代码美化网站

将代码粘贴至操作区, 然后可以将代码复制为图片, 一个很惊艳的网站, 但是有时候网速不是很好, 打开网站并复制图片很慢, 所以计划用swing 构建一个
在这里插入图片描述

需求分析

分析自己比较关注的几个功能.

  1. 语言选择器,文本编辑器需要关联不同代码语言
  2. 主题选择器,文本编辑器需要切换不同风格的主题
  3. 文本编辑器,用来粘贴代码
  4. 复制图片至粘贴板

开发前准备

UI布局
  1. 语言选择器(下拉框), 主题选择器(下拉框),使用JCombox
  2. 文本编辑器, 使用RSyntaxTextArea代码编辑器
    在这里插入图片描述
功能分析
  1. 怎样将文本编辑器转图片
  /**
     * 将组件转换为图片
     *
     * @param component 组件
     * @return 图片信息
     */
    public static BufferedImage exportComp2Image(JComponent component) {
        Rectangle rec = SwingUtilities.getLocalBounds(component);
        BufferedImage img = new BufferedImage(rec.width, rec.height, BufferedImage.TYPE_INT_ARGB);
        Graphics graphics = img.getGraphics();
        component.paint(graphics);
        return img;
    }
  1. 怎样将图片写入至粘贴板
   /**
     * 导出组件为粘贴板图片
     *
     * @param component 组件
     * @param call  回调方法
     */
    public static void exportComp2ClipImage(JComponent component, Runnable call) {
        ClipboardUtil.setImage(exportComp2Image(component));
        SwingUtilities.invokeLater(call);
    }
  1. 怎样将图片保存为本地文件
        exportFile.addActionListener((e) -> {
            int returnValue = saveImageChooser.showSaveDialog(this);
            if (JFileChooser.APPROVE_OPTION == returnValue) {
                File saveFile = saveImageChooser.getSelectedFile();
                String fileName = saveFile.getName();
                if (!fileName.endsWith(".png")) {
                    saveFile = Paths.get(saveFile.getParent(), fileName + ".png").toFile();
                }
                try {
                    BufferedImage bi = SwingCoreUtil.exportComp2Image(bgArea);
                    ImageIO.write(bi, "PNG", saveFile);
                    saveImageChooser.setSelectedFile(new File(""));
                    MessageBuilder.ok(this, "保存成功!");
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        });

实现小技巧

  1. 如何实现长页面, JScrollPane
  2. 如何实现长组件更新父组件滚动
    监听组件的变化事件,更新父组件UI
  3. 复用JFileChooser时,如何在保存后清空上次保存名称
    使用setSelectedFile(new File(“”)) 不要使用setSelectedFile(null);

实现效果

carbon小功能的gif 图
请添加图片描述

swing代码参考



import cn.hutool.core.util.StrUtil;
import cn.note.swing.toolkit.GroupConstants;
import cn.note.swing.core.event.change.ChangeDocumentListener;
import cn.note.swing.core.lifecycle.InitAction;
import cn.note.swing.core.ui.MessageBuilder;
import cn.note.swing.core.ui.rsta.RstaBuilder;
import cn.note.swing.core.ui.rsta.RstaLanguage;
import cn.note.swing.core.ui.rsta.RstaTheme;
import cn.note.swing.core.ui.theme.ButtonFactory;
import cn.note.swing.core.ui.theme.CoreFlatLaf;
import cn.note.swing.core.ui.theme.SvgIconFactory;
import cn.note.swing.core.util.FrameUtil;
import cn.note.swing.core.util.SwingCoreUtil;
import cn.note.swing.core.view.AbstractMigView;
import cn.note.swing.core.view.item.ItemView;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import net.miginfocom.swing.MigLayout;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.filechooser.FileSystemView;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Arrays;


/**
 * 生成代码图片工具类
 */
public class CarbonImageView extends AbstractMigView {
    /**
     * 工具区域
     */
    private JPanel toolArea;
    /**
     * 主题
     */
    private JComboBox<String> themes;
    /**
     * 语言
     */
    private JComboBox<String> langs;
    /**
     * 导出
     */
    private JButton exportImage;
    /**
     * 保存
     */
    private JButton exportFile;
    /**
     * 背景
     */
    private JPanel bgArea;
    /**
     * 图标标题
     */
    private JLabel iconTitle;
    /**
     * 容器
     */
    private RTextScrollPane codePane;

    /**
     * 代码编辑器
     */
    private RSyntaxTextArea rstaEditor;
    /**
     * 保存文件选择器
     */
    private JFileChooser saveImageChooser;

    public CarbonImageView() {
        super(false, true);
    }

    @Override
    protected MigLayout defineMigLayout() {
        return new MigLayout("insets 20 0,gap 0", "[grow]", "[]30[]");
    }

    @Override
    protected void init() {
        // bg
        bgArea = new JPanel(new MigLayout("insets 30,flowy", "[grow,fill]", "[20,fill][grow,fill]"));
        Color bgColor = CoreFlatLaf.ThemeColor.themeColor;
        String style = StrUtil.format("arc:{};background:{};", 20, CoreFlatLaf.toHEXColor(bgColor));
        bgArea.putClientProperty(FlatClientProperties.STYLE, style);

        // title
        FlatSVGIcon svgIcon = new FlatSVGIcon(SvgIconFactory.Tools.carbon_title);
        iconTitle = new JLabel(svgIcon);

        // editor 禁止滚动
        codePane = RstaBuilder.createRsPane();
        codePane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        codePane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
        rstaEditor = (RSyntaxTextArea) codePane.getTextArea();
        rstaEditor.setLineWrap(true);

        // tool
        toolArea = new JPanel(new MigLayout(""));

        // theme
        String[] themeNames = Arrays.stream(RstaTheme.values()).map(Enum::name).toArray(String[]::new);
        themes = new JComboBox<>(themeNames);

        // langs
        String[] langNames = Arrays.stream(RstaLanguage.values()).map(Enum::name).toArray(String[]::new);
        langs = new JComboBox<>(langNames);

        exportFile = ButtonFactory.primaryButton("保存图片");
        exportImage = ButtonFactory.primaryButton("复制图片");

        // save image
        saveImageChooser = new JFileChooser();
        saveImageChooser.setCurrentDirectory(FileSystemView.getFileSystemView().getHomeDirectory());
        FileNameExtensionFilter imageFilter = new FileNameExtensionFilter("png files (*.png)", "png");
        saveImageChooser.setFileFilter(imageFilter);
    }

    @Override
    protected void render() {
        bgArea.add(iconTitle, "w 54!");
        bgArea.add(codePane, "grow");

        toolArea.add(new JLabel("语言:"));
        toolArea.add(langs);

        toolArea.add(new JLabel("主题:"));
        toolArea.add(themes);
        toolArea.add(exportFile, "gapleft 20");
        toolArea.add(exportImage, "gapleft 20");

        view.add(toolArea, "w 80%,center,cell 0 0");
        view.add(bgArea, "w 80%,center,cell 0 1");

        // 默认激活java语言
        langs.setSelectedItem("java");
        setEditorLanguage("java");
    }

    @InitAction("切换主题和code")
    private void toggleThemeAndCode() {
        themes.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                setEditorTheme(e.getItem().toString());
            }
        });

        langs.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                setEditorLanguage(e.getItem().toString());
            }
        });
    }

    /**
     * 设置编辑器主题
     *
     * @param theme 主题类型
     */
    private void setEditorTheme(String theme) {
        RstaTheme.valueOf(theme).getTheme().apply(rstaEditor);
    }

    /**
     * 设置编辑器语言
     *
     * @param lang 语言类型
     */
    private void setEditorLanguage(String lang) {
        RstaLanguage.valueOf(lang).apply(rstaEditor);
    }

    @InitAction("导出至粘贴板")
    private void exportClip() {
        exportImage.addActionListener((e) -> {
            exportImage.requestFocus();
            SwingCoreUtil.exportComp2ClipImage(bgArea, () -> {
                MessageBuilder.ok(this, "已复制图片至粘贴板!");
            });
        });
    }

    @InitAction("保存图片")
    private void exportFile() {
        exportFile.addActionListener((e) -> {
            int returnValue = saveImageChooser.showSaveDialog(this);
            if (JFileChooser.APPROVE_OPTION == returnValue) {
                File saveFile = saveImageChooser.getSelectedFile();
                String fileName = saveFile.getName();
                if (!fileName.endsWith(".png")) {
                    saveFile = Paths.get(saveFile.getParent(), fileName + ".png").toFile();
                }
                try {
                    BufferedImage bi = SwingCoreUtil.exportComp2Image(bgArea);
                    ImageIO.write(bi, "PNG", saveFile);
                    saveImageChooser.setSelectedFile(new File(""));
                    MessageBuilder.ok(this, "保存成功!");
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        });

    }

    @InitAction("控制代码编辑的滚动被父窗口接管")
    private void hideEditorScroll() {
        codePane.getTextArea().getDocument().addDocumentListener(new ChangeDocumentListener() {
            @Override
            public void update(DocumentEvent e) {
                view.revalidate();
                view.repaint();
            }
        });
    }


    public static void main(String[] args) {
        CoreFlatLaf.install();
        FrameUtil.launchBefore(CarbonImageView.class);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值