JTextPane和悬挂缩进毛刺

时间:2012-10-10 10:30:18

标签: java swing jtextpane

我正面临一个令人烦恼的小错误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,由于某种原因,第一行比其他行更大......任何人都有想法如何解决这个问题?

3 个答案:

答案 0 :(得分: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;
        }
    };
}

}

答案 1 :(得分:0)

如果我没错,你可能会使用MetalLookAndFeel, 默认情况下,它使用粗体字体表示许多控件。

试试这个,在ur main方法中关闭金属使用粗体字体;

    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            //Turn off metal's use of bold fonts
        UIManager.put("swing.boldMetal", Boolean.FALSE);
            createAndShowGUI();
        }
    });

答案 2 :(得分: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) {