首页 > 解决方案 > 将图像添加到与线条重叠的 JTextPane 以节省空间

问题描述

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

在此处输入图像描述

我的目标是让图像略微重叠,因此它使用更少的空间。在 MCVE 中,我通过返回较小的垂直跨度来实现这一点,IconView如下所示:

在此处输入图像描述

所以基本上它认为图像不那么高,但无论如何都会完全绘制它。

这主要是有效的,但是有两个问题:

  1. 额外的高度仍然只添加在顶部,如果均匀分布可能会更好。
  2. 但更重要的是,有时底部不会被绘制,例如在上下滚动时:

在此处输入图像描述

很容易理解,因为下半部分并不是图像预期的位置。

我现在的问题是,有没有更好(不那么老套)的方法来防止图像占用这么多空间?或者至少是一种修复绘图的方法?我已经对代码进行了一些研究,但我不确定哪个部分负责决定绘制什么或如何最好地更改它而不弄乱其他任何东西。

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

}

标签: javaswingjtextpanestyleddocument

解决方案


有没有更好(不那么老套)的方法来防止图像占用这么多空间?或者至少是一种修复绘图的方法?

不知道这是否更好或更少hacky(或者它是否会起作用),所以我会让你决定。

也许您可以对图像进行自己的自定义绘画:

  1. 与其将图标添加到文档中,不如添加一个Box.createHorizontalStrut(...)Icon.

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

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

  4. 然后重写paintComponent()文本窗格的方法以遍历HashMap并绘制所有图标。该Position对象将包含添加组件的偏移量。您可以使用 的modelToView(...)方法Document来获取组件在文本窗格中的 x/y 位置。做数学以垂直居中图标,然后绘制它。


推荐阅读