(Java Swing)如何在同一行上创建具有多个图标的JTextPane?

 喵喵-浩_174 发布于 2023-01-19 21:17

正如问题所述,我如何成功地将多个图标放在同一行文本中JTextPane?每当我尝试改变其值时actionText,结果都是非常不可预测的.举个例子,这就是我想要实现的目标:

我想要的酷板游戏文本

如果我只传递图标标记来创建图标(例如""),它们只是叠加在一起(或者可能不是,很难说).如果我输入","或"和",则第一个战斗机图标出现在第一行,而逗号和其他战斗机图标出现在第二行.

我目前正在尝试使用基于oracle教程构建的解决方案JTextPane:JTextPane教程.以下是我创建自定义文本窗格的代码块.

public final class GameTextPaneFactory {

private static final String[] ADVENTURER_TOKENS = {"", "", "", ""};
private static final int TEXT_PANE_WIDTH = 30;

public static JTextPane createActionTextPane(String actionText) {
    ArrayList[] wordsAndStyles = parseActionText(actionText);

    JTextPane actionTextPane = new JTextPane();
    StyledDocument doc = actionTextPane.getStyledDocument();
    addStylesToDocument(doc);

    try {
        for (int i=0; i < wordsAndStyles[0].size(); i++) {
            doc.insertString(doc.getLength(), wordsAndStyles[0].get(i),
                             doc.getStyle(wordsAndStyles[1].get(i)));
        }
    } catch (BadLocationException ble) {
        System.err.println("Couldn't insert initial text into text pane.");
    }

    actionTextPane.setEditable(false);
    return actionTextPane;      
}

private static void addStylesToDocument(StyledDocument doc) {
    // TODO add images (styles) here
    Style def = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
    Style regular = doc.addStyle("regular", def);

    Style icons = doc.addStyle("fighterIcon", regular);
    StyleConstants.setAlignment(icons, StyleConstants.ALIGN_CENTER);
    ImageIcon fighterIcon = new ImageIcon("images/fighter_image.png", "fighter");
    StyleConstants.setIcon(icons, fighterIcon);

    icons = doc.addStyle("clericIcon", regular);
    StyleConstants.setAlignment(icons, StyleConstants.ALIGN_CENTER);
    ImageIcon clericIcon = new ImageIcon("images/cleric_image.png", "cleric");
    StyleConstants.setIcon(icons, clericIcon);

    icons = doc.addStyle("wizardIcon", regular);
    StyleConstants.setAlignment(icons, StyleConstants.ALIGN_CENTER);
    ImageIcon wizardIcon = new ImageIcon("images/wizard_image.png", "wizard");
    StyleConstants.setIcon(icons, wizardIcon);

    icons = doc.addStyle("rogueIcon", regular);
    StyleConstants.setAlignment(icons, StyleConstants.ALIGN_CENTER);
    ImageIcon rogueIcon = new ImageIcon("images/rogue_image.png", "rogue");
    StyleConstants.setIcon(icons, rogueIcon);
}

private static ArrayList[] parseActionText(String text) {
    String[] words = text.split(" ");
    ArrayList outputStrings = new ArrayList();
    ArrayList outputStyles = new ArrayList();
    StringBuilder nextStringBuilder = new StringBuilder();
    int currentLineLength = TEXT_PANE_WIDTH;

    for(String word : words) {                      
        if(Arrays.asList(ADVENTURER_TOKENS).contains(word)) {
            if(nextStringBuilder.length() != 0) {
                outputStrings.add(nextStringBuilder.toString());
                outputStyles.add("regular");
                nextStringBuilder = new StringBuilder();
            }

            outputStrings.add(" "); // this is ignored, but cannot be empty
            switch(word) {
                case "":
                    outputStyles.add("fighterIcon");
                    break;
                case "":
                    outputStyles.add("clericIcon");
                    break;
                case "":
                    outputStyles.add("wizardIcon");
                    break;
                case "":
                    outputStyles.add("rogueIcon");
                    break;
            }

            currentLineLength += 3; // an icon is about 3 characters in length
        } else {
            if(currentLineLength + word.length() + 1 > TEXT_PANE_WIDTH) {
                nextStringBuilder.append("\n");
                currentLineLength = 0;
            }

            nextStringBuilder.append(" " + word);
            currentLineLength += word.length() + 1;
        }
    }

    if(nextStringBuilder.length() != 0) {
        outputStrings.add(nextStringBuilder.toString());
        outputStyles.add("regular");
    }       
    @SuppressWarnings("unchecked")
    ArrayList[] output = new ArrayList[2];
    output[0] = outputStrings;
    output[1] = outputStyles;
    return output;
}

}

如果有人有更好的解决方案,我全都耳朵.谢谢!

1 个回答
  • 尝试添加

    outputStrings.add(" "); // this is ignored, but cannot be empty
    outputStyles.add("regular");
    

    每个新的"冒险家"风格后

    outputStrings.add(" "); // this is ignored, but cannot be empty
    switch (word) {
        case "<FIGHTER>":
            outputStyles.add("fighterIcon");
            break;
        case "<CLERIC>":
            outputStyles.add("clericIcon");
            break;
        case "<WIZARD>":
            outputStyles.add("wizardIcon");
            break;
        case "<ROGUE>":
            outputStyles.add("rogueIcon");
            break;
    }
    outputStrings.add(" "); // this is ignored, but cannot be empty
    outputStyles.add("regular");
    

    更新

    我有点玩,看看我是否可以让格式看起来好一点,这基本上就是我想出来的......

    冒险

    我基本上将文本和图像直接插入文本窗格,而不是使用样式.似乎有类似样式的问题彼此相邻设置,所以相反,它们基本上合并到文档中的单个条目,这可以解释为什么你的样式有问题.出于某种原因,我在图标上遇到了类似的问题,因此我每次都必须创建一个新实例...

    这有点粗糙和准备,但基本的想法是存在的.它基本上使用正则表达式API来查找"关键字"的所有匹配项,在它之前插入文本,然后根据关键字插入一个特殊图标...

    public static JTextPane createActionTextPane(String actionText) {
        JTextPane actionTextPane = new JTextPane();
        actionTextPane.setOpaque(false);
    
        StyledDocument doc = actionTextPane.getStyledDocument();
    
        Pattern pattern = Pattern.compile("<FIGHTER>|<CLERIC>|<GOLD>");
        Matcher matcher = pattern.matcher(actionText);
        int previousMatch = 0;
        while (matcher.find()) {
    
            int startIndex = matcher.start();
            int endIndex = matcher.end();
            String group = matcher.group();
    
            String subText = actionText.substring(previousMatch, startIndex);
            if (!subText.isEmpty()) {
                actionTextPane.replaceSelection(subText);
            } 
            switch (group) {
                case "<FIGHTER>":
                    actionTextPane.insertIcon(new ImageIcon("fifight.gif"));
                    break;
                case "<CLERIC>":
                    actionTextPane.insertIcon(new ImageIcon("mage.gif"));
                    break;
                case "<GOLD>":
                    actionTextPane.insertIcon(new ImageIcon("Gold.png"));
                    break;
            }
    
            previousMatch = endIndex;
    
        }
        String subText = actionText.substring(previousMatch);
        if (!subText.isEmpty()) {
            actionTextPane.replaceSelection(subText);
        }
    
        actionTextPane.setEditable(false);
        return actionTextPane;
    }
    

    现在,坦率地说,我并没有对线宽等感到困扰,相反,使用了a JScrollPane和它JTextComponent的包装功能......但这取决于你......

    2023-01-19 21:21 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有