Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/374.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在JTextPane中对齐和内联组件(或图标)_Java_Swing_Jtextpane - Fatal编程技术网

Java 在JTextPane中对齐和内联组件(或图标)

Java 在JTextPane中对齐和内联组件(或图标),java,swing,jtextpane,Java,Swing,Jtextpane,我正在用Java开发一个应用程序,该应用程序除其他外,应该显示一个魔术的细节:在文本字段中显示采集卡(因为我使用的是Swing,所以它目前是一个JTextPane) 这些细节包含文本和小图标,其中一些图标与文本内联(因此文本围绕着它们流动),根据我的设计,一些图标与同一行中的一些左对齐文本右对齐 我从另一个应用程序中获取了一个图像,该应用程序使用了与我正在开发的应用程序非常相似的设计(尽管不是Java): 基本上应该是这样的 现在,出于对一切的热爱,我不能让它在JTextPane中工作 我开始

我正在用Java开发一个应用程序,该应用程序除其他外,应该显示一个魔术的细节:在文本字段中显示采集卡(因为我使用的是Swing,所以它目前是一个
JTextPane

这些细节包含文本和小图标,其中一些图标与文本内联(因此文本围绕着它们流动),根据我的设计,一些图标与同一行中的一些左对齐文本右对齐

我从另一个应用程序中获取了一个图像,该应用程序使用了与我正在开发的应用程序非常相似的设计(尽管不是Java):

基本上应该是这样的

现在,出于对一切的热爱,我不能让它在
JTextPane
中工作

我开始尝试用CSS实现这一点,但发现
JEditorPane
和子类不支持“float”属性,因此我尝试使用该窗格的
StyledDocument

起初,我没有让第一个部分工作(顶部的图标和右对齐文本总是忽略它们的对齐方式,并直接放在行中左对齐文本的后面),直到我找到

建议使用以下代码行:

pane.setEditorKit(new HTMLEditorKit());
这确实解决了我的第一个问题

但现在我被困在第二部分,让第二部分中的图标与文本对齐。以下是我目前得到的信息:

我发现,出于某种原因,当您使用带有上述代码行的编辑器套件将
JTextPane
切换到html模式时,插入组件简直是疯了

顶部的图标(实际上我已合并为一个图像)和下面文本中的图标(未合并)都位于
JLabels
的内部,但无论我是将它们添加为图像还是
JLabels
的内部。图片或标签绝对不会比你看到的大,我根本不知道额外的空白是从哪里来的

我发现了,答案表明这是某种bug,或者只是
JEditorPane
的html模式的奇怪行为

如果我再次删除上述代码行,我最终会遇到原来的问题:

根据图标在文本中的确切位置,我会得到各种不同的奇怪结果。我将下面的更多示例图片放在一起:

那么,我怎么可能解决这个问题呢?除了这一部分外,
JTextPane
对我来说运行良好,但我可以使用其他解决方案,只要最终结果看起来仍然一样。请记住,我可能希望在其中添加一些其他组件(如按钮),因此如果可能的话,我希望使用Swing的原生组件

用户将无法编辑TextPane的内容,但我想稍后添加一个选项,在一次单击中复制所有内容(因此我宁愿保留文本区域)

下面,我为您提供了一个(不是很简单的)工作示例:

(编辑:现在在底部更新了代码!旧代码仍在下面的链接下。)

下面是我正在使用的图标。您需要重命名它们并更改代码中的路径。




需要注意的一些事项:

  • 开始时,我使用“Style”类对文本进行了样式设置,如Oracle网站上的“如何使用编辑器窗格和文本窗格”教程(textsampledemo.java,供您参考)所述。有了这个,我甚至不能在顶部做右对齐部分。奇怪的是,当我使用'SimpleAttributeSet'类进行样式设置时,即使设置非常相同,它也能工作
  • 我尝试了文本和包含图标的标签的不同对齐选项。无论我使用什么选项,都没有明显的区别。

更新1: 在Sharcoux的回答之后,我编辑了我的代码,在实际的JTextPane上方有2个JLabel,其中包含两行,这两行应该具有不同的对齐方式(左对齐部分和右对齐部分)。 JTextPane现在不再使用HTMLEditorKit,我使用insertIcon()将图标插入到文本中

这样,图标插入(几乎)正确
此处图像:

然而,有两件小事我仍然不满意:

第一:
我需要将所有内容放入JScrollPane中,因为在我的实际应用程序中,TextPane中的文本要长得多。因为我现在有三个组件,而不仅仅是文本窗格,所以我需要将所有内容都放到JPanel中,然后再放到滚动窗格中。
但是,如果您这样做,JTextPane就不知道它的with值不应该再超过JScrollPane值。它会停止包装文本,并与整个文本一样大。
我为此提出了一个新问题,因为我觉得这是Swing的一个基本问题,应该有自己的问题。如果您想提供帮助,请访问以下链接:

秒:
这可能是不可能的,但我想我还是会问。以这种方式添加图标时,图标与文本具有相同的基线。他们有没有办法把它们移低一点?可能是2-3像素?这样,它们将更好地与文本对齐。下面是两张图片。
这就是它现在的样子:

这就是我想要的样子:

也许我可以对JTextPane的某些部分进行子类化和重写,以便将其上呈现的所有图标向下移动一个设定的像素量,或者类似的东西

作为参考,这里也是我新的,更新的代码。如果你还想看的话,我用一个pastebin.com链接替换了上面的旧链接


更新2: 我的第一个问题已经解决了!我也更新了下面的代码来反映这一点

我的第二个问题仍然有效
import java.awt.EventQueue;
import java.awt.Graphics2D;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
import javax.swing.JTextPane;
import javax.swing.JViewport;
 
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import javax.swing.ScrollPaneConstants;
 
public class MinimalExample extends JFrame {
 
    private JPanel contentPane;
    private JScrollPane scrollPane;
    private JTextPane textPane;
 
    // Setup some data for an example card:
    String name = "Absorb Vis";
    String set = "CON";
    String manaCost = "{6}{B}";
    String printedType = "Sorcery";
    String artist = "Brandon Kitkouski";
 
    String rulesText = "Target player loses 4 life and you gain 4 life.\n"
            + "Basic landcycling {1}{B} ({1}{B}, Discard this card: "
            + "Search your library for a basic land card, reveal it, and put "
            + "it into your hand. Then shuffle your library.)";
 
    HashMap<String, BufferedImage> manaSymbolImages;
    private ScrollablePanel textPanel;
    //private JPanel textPanel;
    private JPanel headlinesPanel;
    private JPanel firstHeadlinePanel;
    private JPanel secondHeadlinePanel;
    private JLabel titleLabel;
    private JLabel manaCostLabel;
    private JLabel typeLabel;
    private JLabel setLabel;
 
    public static void main(String[] args) {
 
        EventQueue.invokeLater(new Runnable() {
 
            public void run() {
 
                try {
                    MinimalExample frame = new MinimalExample();
                    frame.setVisible(true);
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
 
    public MinimalExample() {
 
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 230, 400);
        contentPane = new JPanel();
        contentPane.setBackground(Color.WHITE);
        contentPane.setBorder(null);
        setContentPane(contentPane);
        /* HTMLEditorKit eKit = new HTMLEditorKit();
         * textPane.setEditorKit(eKit); HTMLDocument htmlDoc = (HTMLDocument)
         * textPane.getDocument(); htmlDoc.setPreservesUnknownTags(false); */
        contentPane.setLayout(new GridLayout(0, 1, 0, 0));
 
        textPanel = new ScrollablePanel();
        //textPanel = new JPanel();
        textPanel.setBackground(Color.WHITE);
        textPanel.setLayout(new BorderLayout(0, 0));
 
        headlinesPanel = new JPanel();
        headlinesPanel.setBorder(new EmptyBorder(2, 5, 3, 5));
        headlinesPanel.setBackground(Color.WHITE);
        textPanel.add(headlinesPanel, BorderLayout.NORTH);
        headlinesPanel.setLayout(new GridLayout(0, 1, 0, 0));
 
        firstHeadlinePanel = new JPanel();
        firstHeadlinePanel.setBorder(new EmptyBorder(0, 0, 3, 0));
        firstHeadlinePanel.setOpaque(false);
        headlinesPanel.add(firstHeadlinePanel);
        firstHeadlinePanel.setLayout(new BorderLayout(0, 0));
 
        titleLabel = new JLabel("");
        titleLabel.setFont(new Font("Tahoma", Font.BOLD, 12));
        firstHeadlinePanel.add(titleLabel, BorderLayout.WEST);
 
        manaCostLabel = new JLabel("");
        firstHeadlinePanel.add(manaCostLabel, BorderLayout.EAST);
 
        secondHeadlinePanel = new JPanel();
        secondHeadlinePanel.setBorder(null);
        secondHeadlinePanel.setOpaque(false);
        headlinesPanel.add(secondHeadlinePanel);
        secondHeadlinePanel.setLayout(new BorderLayout(0, 0));
 
        typeLabel = new JLabel("");
        typeLabel.setFont(new Font("Tahoma", Font.PLAIN, 12));
        secondHeadlinePanel.add(typeLabel, BorderLayout.WEST);
 
        setLabel = new JLabel("");
        setLabel.setFont(new Font("Tahoma", Font.BOLD, 12));
        secondHeadlinePanel.add(setLabel, BorderLayout.EAST);
 
        scrollPane = new JScrollPane();
        scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        scrollPane.setBackground(Color.WHITE);
        contentPane.add(scrollPane);
 
        textPane = new JTextPane();
        textPane.setBorder(new EmptyBorder(0, 3, 0, 3));
        textPane.setAlignmentY(0.3f);
        textPane.setEditable(false);
 
        textPanel.add(textPane, BorderLayout.CENTER);
 
        scrollPane.setViewportView(textPanel);
 
        loadManaCostIcons();
        setPaneText();
    }
 
    // This part inserts the text into the document of the text pane.
    public void setPaneText() {
 
        titleLabel.setText(name);
        manaCostLabel.setIcon(combineSymbols(manaCost));
        typeLabel.setText(printedType);
        setLabel.setText(set);
 
        StyledDocument textPaneDoc = textPane.getStyledDocument();
 
        SimpleAttributeSet defaultAtts = new SimpleAttributeSet();
        StyleConstants.setFontFamily(defaultAtts, "SansSerif");
        StyleConstants.setFontSize(defaultAtts, 12);
 
        SimpleAttributeSet rulesAtts = new SimpleAttributeSet(defaultAtts);
 
        SimpleAttributeSet artistAtts = new SimpleAttributeSet(defaultAtts);
        StyleConstants.setFontSize(artistAtts, 10);
 
        addTextWithSymbols(rulesText, rulesAtts);
 
        try {
 
            textPaneDoc.insertString(textPaneDoc.getLength(), artist, artistAtts);
        }
        catch (BadLocationException e) {
 
            e.printStackTrace();
        }
 
        textPane.revalidate();
        textPane.repaint();
    }
 
    /* This adds the rest of the text to the pane. The codes for the symbols get
     * replaced by the actual symbols and the text gets inserted piece by piece. */
    public void addTextWithSymbols(String text, SimpleAttributeSet style) {
 
        StyledDocument textPaneDoc = textPane.getStyledDocument();
        Pattern symbolPattern = Pattern.compile("\\{(.*?)\\}");
 
        try {
 
            Matcher symbolMatcher = symbolPattern.matcher(text);
            int previousMatch = 0;
 
            while (symbolMatcher.find()) {
 
                int start = symbolMatcher.start();
                int end = symbolMatcher.end();
                String subStringText = text.substring(previousMatch, start);
                String currentMatch = text.substring(start, end);
 
                if (subStringText.isEmpty() == false) {
 
                    textPaneDoc.insertString(textPaneDoc.getLength(), subStringText, style);
                }
 
                ImageIcon currentIcon = new ImageIcon(manaSymbolImages.get(currentMatch));
 
                SimpleAttributeSet iconAtts = new SimpleAttributeSet();
                JLabel iconLabel = new JLabel(currentIcon);
                StyleConstants.setComponent(iconAtts, iconLabel);
 
                textPane.insertIcon(currentIcon);
                previousMatch = end;
            }
 
            String subStringText = text.substring(previousMatch);
 
            if (subStringText.isEmpty() == false) {
               
                textPaneDoc.insertString(textPaneDoc.getLength(), subStringText + "\n", style);
            }
        }
        catch (Exception e) {
 
            e.printStackTrace();
        }
    }
 
    /* Everything below is more or less irrelevant. However, you might need to
     * adjust the image image file paths. */
 
    public void loadManaCostIcons() {
 
        manaSymbolImages = new HashMap<String, BufferedImage>();
        try {
 
            // Most likely, those paths won't work for you!
            File bFile = new File("resource/B.png");
            File c1File = new File("resource/1.png");
            File c6File = new File("resource/6.png");
 
            manaSymbolImages.put("{B}", ImageIO.read(bFile));
            manaSymbolImages.put("{1}", ImageIO.read(c1File));
            manaSymbolImages.put("{6}", ImageIO.read(c6File));
        }
        catch (IOException e) {
 
            e.printStackTrace();
        }
    }
 
    public ImageIcon combineSymbols(String symbols) {
 
        String[] manaSymbols = symbols.split("(?<=})");
        int combinedWidth = 0;
        int maxHeight = 0;
 
        for (int i = 0; i < manaSymbols.length; i++) {
 
            BufferedImage currentSymbolImage = manaSymbolImages.get(manaSymbols[i]);
            combinedWidth += currentSymbolImage.getWidth();
 
            if (maxHeight < currentSymbolImage.getWidth()) {
                maxHeight = currentSymbolImage.getWidth();
            }
        }
 
        BufferedImage combinedManaCostImage = new BufferedImage(combinedWidth, maxHeight,
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D graphics = combinedManaCostImage.createGraphics();
 
        int currentPosition = 0;
 
        for (int i = 0; i < manaSymbols.length; i++) {
 
            BufferedImage tempCurrentImage = manaSymbolImages.get(manaSymbols[i].trim());
            graphics.drawImage(tempCurrentImage, null, currentPosition, 0);
            currentPosition += tempCurrentImage.getWidth();
        }
 
        graphics.dispose();
        return (new ImageIcon(combinedManaCostImage));
    }
 
    /* Original source of this is here:
     * https://stackoverflow.com/questions/15783014/jtextarea-on-jpanel-inside-jscrollpane-does-not-resize-properly
     * And one update to it is here:
     *  */
    private static class ScrollablePanel extends JPanel implements Scrollable {
 
        @Override
        public Dimension getPreferredScrollableViewportSize() {
 
            return super.getPreferredSize();
        }
 
        @Override
        public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
                int direction) {
 
            return 16;
        }
 
        @Override
        public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation,
                int direction) {
 
            return 16;
        }
 
        @Override
        public boolean getScrollableTracksViewportWidth() {
 
            return true;
        }
 
        @Override
        public boolean getScrollableTracksViewportHeight() {
 
            boolean track = true;
            Container parent = getParent();
            if (parent instanceof JViewport) {
 
                JViewport viewport = (JViewport) parent;
                if (viewport.getHeight() < getPreferredSize().height) {
                    track = false;
                }
 
            }
 
            return track;
        }
    }
}
public class Test {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame mainFrame = new JFrame("test");
                mainFrame.setSize(300, 100);
                mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                Container pane = mainFrame.getContentPane();
                pane.setLayout(new BorderLayout());

                JTP jtp = new JTP();
                pane.add(jtp);

                mainFrame.setVisible(true);
            }
        });
    }

    static class JTP extends JTextPane {

        JTP() {
            HTMLEditorKit eKit = new HTMLEditorKit();
            setEditorKit(eKit);
            HTMLDocument htmlDoc = (HTMLDocument) getDocument();//the HTMLEditorKit automatically created an HTMLDocument
            htmlDoc.setPreservesUnknownTags(false);//I advice you to put this line if you plan to insert some foreign HTML

            //inserting plain text (just change null for an attributeSet for styled text)
            try {
                htmlDoc.insertString(0, "test", null);
            } catch (BadLocationException ex) {
                Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
            }

            //inserting images
            insertIcon(new ImageIcon("image.png"));
            insertIcon(new ImageIcon("image.png"));

            //inserting components (With component, you should specify the yAlignment by yourself)
            JLabel label = new JLabel(new ImageIcon("image.png"));
            label.setAlignmentY(JLabel.TOP);
            insertComponent(label);
        }

    }

}
public class Test {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame mainFrame = new JFrame("test");
                mainFrame.setSize(300, 100);
                mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                Container pane = mainFrame.getContentPane();
                pane.setLayout(new BorderLayout());
                pane.setBackground(Color.WHITE);

                pane.add(new JTP());
                pane.add(new Title(), BorderLayout.NORTH);

                mainFrame.setVisible(true);
            }
        });
    }

    static class JTP extends JTextPane {

        JTP() {
            setEditable(false);
            setOpaque(false);

            HTMLEditorKit eKit = new HTMLEditorKit();
            setEditorKit(eKit);
            HTMLDocument htmlDoc = (HTMLDocument) getDocument();//the HTMLEditorKit automatically created an HTMLDocument
            htmlDoc.setPreservesUnknownTags(false);//I advice you to put this line if you plan to insert some foreign HTML

            //inserting plain text (just change null for an attributeSet for styled text)
            try {
                htmlDoc.insertString(0, "capacity : ", null);
            } catch (BadLocationException ex) {
                Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
            }

            //inserting images
            insertIcon(new ImageIcon("image.png"));
            insertIcon(new ImageIcon("image.png"));

            //inserting components (With component, you should specify the yAlignment by yourself)
            JLabel label = new JLabel(new ImageIcon("image.png"));
            label.setAlignmentY(JLabel.TOP);
            insertComponent(label);
        }

    }

    static class Title extends JPanel {

        Title() {
            setLayout(new BorderLayout());
            setOpaque(false);
            add(new JLabel("<html><b>Card title</b></html>"), BorderLayout.CENTER);
            add(new JLabel(new ImageIcon("image.png")), BorderLayout.EAST);
        }

    }

}