Carbon 代码美化网站
将代码粘贴至操作区, 然后可以将代码复制为图片, 一个很惊艳的网站, 但是有时候网速不是很好, 打开网站并复制图片很慢, 所以计划用swing 构建一个
需求分析
分析自己比较关注的几个功能.
- 语言选择器,文本编辑器需要关联不同代码语言
- 主题选择器,文本编辑器需要切换不同风格的主题
- 文本编辑器,用来粘贴代码
- 复制图片至粘贴板
开发前准备
UI布局
- 语言选择器(下拉框), 主题选择器(下拉框),使用JCombox
- 文本编辑器, 使用RSyntaxTextArea代码编辑器
功能分析
- 怎样将文本编辑器转图片
/**
* 将组件转换为图片
*
* @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;
}
- 怎样将图片写入至粘贴板
/**
* 导出组件为粘贴板图片
*
* @param component 组件
* @param call 回调方法
*/
public static void exportComp2ClipImage(JComponent component, Runnable call) {
ClipboardUtil.setImage(exportComp2Image(component));
SwingUtilities.invokeLater(call);
}
- 怎样将图片保存为本地文件
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();
}
}
});
实现小技巧
- 如何实现长页面, JScrollPane
- 如何实现长组件更新父组件滚动
监听组件的变化事件,更新父组件UI - 复用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);
}
}