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

时间:2014-03-05 05:01:39

标签: java swing icons jtextpane

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

Cool board game text I'd like to have

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

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

public final class GameTextPaneFactory {

private static final String[] ADVENTURER_TOKENS = {"<FIGHTER>", "<CLERIC>", "<WIZARD>", "<ROGUE>"};
private static final int TEXT_PANE_WIDTH = 30;

public static JTextPane createActionTextPane(String actionText) {
    ArrayList<String>[] 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<String>[] parseActionText(String text) {
    String[] words = text.split(" ");
    ArrayList<String> outputStrings = new ArrayList<String>();
    ArrayList<String> outputStyles = new ArrayList<String>();
    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 "<FIGHTER>":
                    outputStyles.add("fighterIcon");
                    break;
                case "<CLERIC>":
                    outputStyles.add("clericIcon");
                    break;
                case "<WIZARD>":
                    outputStyles.add("wizardIcon");
                    break;
                case "<ROGUE>":
                    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<String>[] output = new ArrayList[2];
    output[0] = outputStrings;
    output[1] = outputStyles;
    return output;
}

}

如果某人有更好的解决方案,我会全力以赴。谢谢!

2 个答案:

答案 0 :(得分:6)

尝试添加

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");

<强>更新

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

Adventure

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

这有点粗糙和准备,但基本的想法是存在的。它基本上使用正则表达式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;
}

现在,坦率地说,我并没有对线宽等感到困扰,而是使用了JScrollPaneJTextComponent的包裹功能......但这取决于你。 ..

答案 1 :(得分:4)

结帐Auto Replace Smiles Text With Appropriate Images。可能不完全不是你想要的,但它应该给你一些想法