3

我正面临 JTextPane 和悬挂缩进的恼人小错误。

这是一个简单的例子:

public class Scrap {

public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(200, 200);
    frame.setLayout(new BorderLayout());

    JTextPane textPane = new JTextPane();

    JScrollPane scroll = new JScrollPane(textPane);

    frame.add(scroll);

    StyledDocument doc = (StyledDocument) textPane.getDocument();

    try {

        String str = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum ";

        doc.insertString(doc.getLength(), str, null);

        // Hanging indent
        MutableAttributeSet mas = new SimpleAttributeSet();
        StyleConstants.setLeftIndent(mas, 20);
        StyleConstants.setFirstLineIndent(mas, -20);
        doc.setParagraphAttributes(0, str.length(), mas, false);

    } catch (BadLocationException e) {
        e.printStackTrace();
    }

    frame.setVisible(true);
    frame.setLocationRelativeTo(null);
}
}

在我的计算机上,使用 Java 7,由于某种原因,第一行比其他行更粗......有人知道如何解决这个问题吗?

4

3 回答 3

1

我回到了这个,我把它修好了!至少足以满足我的需求。正如我所怀疑的那样,问题在于 JTextPane 两次绘制了第一行。

Oracle 方便地忽略了我的错误报告,我猜他们只是不再关心 Swing。

这是修复(包括我从某处找到的 Java 7 的长自动换行修复):

import javax.swing.*;
import javax.swing.text.Element;
import javax.swing.text.ParagraphView;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.InlineView;
import java.awt.*;

/**
 * A fixed HTML Editor Kit, which fixes two things:
 * - Word wrapping of long words (bugged in Java 7)
 * - A hanging indent bug
 */
public class FixedHtmlEditorKit extends HTMLEditorKit {

@Override
public ViewFactory getViewFactory() {
    return new HTMLEditorKit.HTMLFactory() {
        public View create(Element e) {
            View v = super.create(e);

            if (v instanceof InlineView) {
                return new InlineView(e) {
                    public int getBreakWeight(int axis, float pos, float len) {
                        return GoodBreakWeight;
                    }

                    public View breakView(int axis, int p0, float pos, float len) {
                        if (axis == View.X_AXIS) {
                            checkPainter();
                            int p1 = getGlyphPainter().getBoundedPosition(this, p0, pos, len);
                            if (p0 == getStartOffset() && p1 == getEndOffset()) {
                                return this;
                            }
                            return createFragment(p0, p1);
                        }
                        return this;
                    }
                };
            }
            else if (v instanceof ParagraphView) {
                return new ParagraphView(e) {
                    protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
                        if (r == null) {
                            r = new SizeRequirements();
                        }
                        float pref = layoutPool.getPreferredSpan(axis);
                        float min = layoutPool.getMinimumSpan(axis);
                        // Don't include insets, Box.getXXXSpan will include them.
                        r.minimum = (int) min;
                        r.preferred = Math.max(r.minimum, (int) pref);
                        r.maximum = Integer.MAX_VALUE;
                        r.alignment = 0.5f;
                        return r;
                    }

                    private boolean allowedToPaintFirstView = true;

                    private float tabBase;

                    /*
                     * We need to override this since tabBase is private in ParagraphView.
                     */
                    @Override
                    protected float getTabBase() {
                        return tabBase;
                    }

                    @Override
                    protected void paintChild(Graphics g, Rectangle alloc, int index) {
                        // Don't paint the first index twice!
                        if (index == 0 && !allowedToPaintFirstView) {
                            return;
                        }
                        super.paintChild(g, alloc, index);
                    }


                    public void paint(Graphics g, Shape a) {
                        Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();

                        tabBase = alloc.x + getLeftInset();

                        // line with the negative firstLineIndent value needs
                        // special handling
                        if (firstLineIndent < 0) {
                            Shape sh = getChildAllocation(0, a);
                            if ((sh != null) &&  sh.intersects(alloc)) {
                                int x = alloc.x + getLeftInset() + firstLineIndent;
                                int y = alloc.y + getTopInset();

                                Rectangle clip = g.getClipBounds();
                                Rectangle tempRect = new Rectangle();
                                tempRect.x = x + getOffset(X_AXIS, 0);
                                tempRect.y = y + getOffset(Y_AXIS, 0);
                                tempRect.width = getSpan(X_AXIS, 0) - firstLineIndent;
                                tempRect.height = getSpan(Y_AXIS, 0);
                                if (tempRect.intersects(clip)) {
                                    tempRect.x = tempRect.x - firstLineIndent;
                                    allowedToPaintFirstView = true;
                                    paintChild(g, tempRect, 0);
                                    allowedToPaintFirstView = false;
                                }
                            }
                        }

                        super.paint(g, a);
                    }
                };
            }
            return v;
        }
    };
}

}

于 2013-01-07T12:54:12.637 回答
0

如果我没记错的话,您可能正在使用 MetalLookAndFeel,它在默认情况下对许多控件使用粗体字体。

试试这个,在你的主要方法中关闭金属对粗体字体的使用;

    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            //Turn off metal's use of bold fonts
        UIManager.put("swing.boldMetal", Boolean.FALSE);
            createAndShowGUI();
        }
    });
于 2012-10-10T11:23:33.367 回答
0

差不多五年后,这段代码派上了用场。但是,我们确实遇到了一个问题,即在设置制表位时初始绘制显示错误的制表符行为。这可能是特定于 Java 8 的,或者它可能只是在正确的时间发生重绘的应用程序中很容易隐藏。

我们通过反射将 tabBase 强制注入父级解决了这个问题,因为扩展类的任何进一步尝试变得复杂。

这是已接受答案的修改部分

@Override
public void paint(Graphics g, Shape a) {
    final Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();

    tabBase = alloc.x + getLeftInset();

    // Set the tabBase into the parent ParagraphView - not all its use getTabBase() but the parent
    // handles some of the painting, so it needs to have this value set properly
    try {
        final Field parentTabBase = ParagraphView.class.getDeclaredField("tabBase");
        parentTabBase.setAccessible(true);
        parentTabBase.set(this, tabBase);
        parentTabBase.setAccessible(false);
    } catch (NoSuchFieldException | IllegalAccessException e) {
        throw new RuntimeException("Error encountered setting tabBase", e);
    }

    // line with the negative firstLineIndent value needs
    // special handling
    if (firstLineIndent < 0) {
于 2017-09-25T20:55:07.110 回答