将图像添加到与行重叠的JTextPane中以节省空间

时间:2018-08-03 15:39:41

标签: java swing jtextpane styleddocument

我有一个带有StyledDocument的JTextPane,需要在文本之间添加一个比字体高的图像。这意味着线通常会变高:

enter image description here

我的目标是使图像稍微重叠,以便使用更少的空间。在MCVE中,我通过为IconView返回较小的垂直跨度来实现此目的,如下所示:

enter image description here

因此从基本上讲,它认为图像的高度较低,但无论如何都将其完全绘制。

这通常可行,但是有两个问题:

  1. 附加高度仍然仅添加在顶部,如果它均匀分布,可能会更好。
  2. 但更重要的是,有时未绘制底部,例如在向上滚动然后再次向下滚动时:

enter image description here

可以理解,因为下部实际上并不是图像的预期位置。

我的问题是,现在有没有一种更好的方法(减少黑客攻击)来防止图像占用太多空间?或者至少是一种修复图形的方法?我已经对代码进行了一些研究,但是我不确定哪个部分负责确定绘制的内容或如何最好地更改它而不弄乱其他任何内容。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import javax.swing.text.BoxView;
import javax.swing.text.ComponentView;
import javax.swing.text.Element;
import javax.swing.text.IconView;
import javax.swing.text.LabelView;
import javax.swing.text.ParagraphView;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;

public class OverlappingImage {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            createGui();
        });
    }

    private static void createGui() {
        JTextPane textPane = new JTextPane();
        textPane.setEditorKit(new MyEditorKit());
        textPane.setEditable(false);
        textPane.setPreferredSize(new Dimension(320, 200));

        StyledDocument doc = textPane.getStyledDocument();
        SimpleAttributeSet iconStyle = new SimpleAttributeSet();
        StyleConstants.setIcon(iconStyle, createImage());

        try {
            doc.insertString(doc.getLength(), TEST_TEXT+"\n"+TEST_TEXT, null);
            doc.insertString(doc.getLength(), "Image", iconStyle);
            doc.insertString(doc.getLength(), TEST_TEXT, null);
        } catch (BadLocationException ex) {
            Logger.getLogger(OverlappingImage.class.getName()).log(Level.SEVERE, null, ex);
        }

        JFrame window = new JFrame();
        window.add(new JScrollPane(textPane), BorderLayout.CENTER);
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.pack();
        window.setLocationByPlatform(true);
        window.setTitle("Test");
        window.setVisible(true);
    }

    static class MyEditorKit extends StyledEditorKit {

        private final ViewFactory factory;

        public MyEditorKit() {
            this.factory = new StyledViewFactory();
        }

        @Override
        public ViewFactory getViewFactory() {
            return factory;
        }

        static class StyledViewFactory implements ViewFactory {

            @Override
            public View create(Element elem) {
                String kind = elem.getName();
                if (kind != null) {
                    if (kind.equals(AbstractDocument.ContentElementName)) {
                        return new LabelView(elem);
                    } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
                        return new ParagraphView(elem);
                    } else if (kind.equals(AbstractDocument.SectionElementName)) {
                        return new BoxView(elem, View.Y_AXIS);
                    } else if (kind.equals(StyleConstants.ComponentElementName)) {
                        return new ComponentView(elem);
                    } else if (kind.equals(StyleConstants.IconElementName)) {
                        return new MyIconView(elem);
                    }
                }
                return new LabelView(elem);
            }

        }
    }

    static class MyIconView extends IconView {

        public MyIconView(Element elem) {
            super(elem);
        }

        @Override
        public float getPreferredSpan(int axis) {
            if (axis == View.Y_AXIS) {
                float height = super.getPreferredSpan(axis);
                return height * 0.7f;
            }
            return super.getPreferredSpan(axis);
        }
    }

    /**
     * Creates the example image.
     */
    public static ImageIcon createImage() {
        BufferedImage image = new BufferedImage(28,28, BufferedImage.TYPE_INT_ARGB);
        Graphics g = image.getGraphics();
        g.setColor(Color.GREEN);
        g.fillRect(0, 0, 28, 28);
        g.setColor(Color.BLACK);
        g.drawRect(0, 0, 27, 27);
        g.dispose();
        return new ImageIcon(image);
    }

    private static final String TEST_TEXT = "Lorem ipsum dolor sit amet, "
            + "consectetur adipisici elit, sed eiusmod tempor incidunt ut "
            + "labore et dolore magna aliqua. Ut enim ad minim veniam, quis "
            + "nostrud exercitation ullamco laboris nisi ut aliquid ex ea "
            + "commodi consequat. Quis aute iure reprehenderit in voluptate "
            + "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur "
            + "sint obcaecat cupiditat non proident, sunt in culpa qui officia "
            + "deserunt mollit anim id est laborum.";

}

1 个答案:

答案 0 :(得分:0)

  

是否有更好的方法(减少黑客攻击)来防止图像占用太多空间?或者至少是一种修复图形的方法?

不知道这是更好还是更hacky(或者是否行得通),所以我让您决定。

也许您可以对图像进行自定义绘制:

  1. 不是向图标添加图标,而是可以添加Box.createHorizontalStrut(...)来为Icon的宽度保留空间。

  2. 然后,您可以使用文档的createPosition()方法获取应添加Icon的位置。

  3. 您创建一个HashMap来存储PositionIcon对象。

  4. 然后,您将覆盖文本窗格的paintComponent()方法以遍历HashMap并绘制所有图标。 Position对象将包含添加组件的位置的偏移量。您可以使用modelToView(...)的{​​{1}}方法在文本窗格中获取组件的x / y位置。进行数学运算以使Icon垂直居中,然后进行绘制。