Java 如何:JTextPane自动换行不是在单词边界(如“空格”)上,而是在字母边界上

Java 如何:JTextPane自动换行不是在单词边界(如“空格”)上,而是在字母边界上,java,swing,jscrollpane,jtextpane,Java,Swing,Jscrollpane,Jtextpane,我将JTextPane放入JScrollPane。正如我所尝试的,如果JTextPane超过显示区域的宽度,它将自动换行。自动换行是基于单词边界的,例如空格字符 我的内容包含很多空间。我想逐字逐句地展示它。所以我需要自动换行,但我希望它只发生在显示区域的最大宽度上,而不是在单词边界上 怎么做 我所尝试的: 将所有空格替换为“\0”,因此,从字面上看,我的内容是一个大字。 加1 下面是我阅读后失败的尝试。 我不是在责怪他的解决方案,因为我的情况与他的不完全相同 StanislavL的解决方案要求一

我将JTextPane放入JScrollPane。正如我所尝试的,如果JTextPane超过显示区域的宽度,它将自动换行。自动换行是基于单词边界的,例如空格字符

我的内容包含很多空间。我想逐字逐句地展示它。所以我需要自动换行,但我希望它只发生在显示区域的最大宽度上,而不是在单词边界上

怎么做

我所尝试的:

将所有空格替换为“\0”,因此,从字面上看,我的内容是一个大字。 加1 下面是我阅读后失败的尝试。 我不是在责怪他的解决方案,因为我的情况与他的不完全相同

StanislavL的解决方案要求一行至少包含2个LabelView。据他说,这是Swing的布局方法实现强加的,其中强制打断仅在行视图有多个子视图时有效。因此,StanislavL故意为字符\r指定了一个特殊属性,以确保单独的LabelView。并将\r用作包装的标志。但是在我的场景中,我不能在我的内容中插入任何字符

我的想法很简单,只需为StyledEditorKit提供ViewFactory的自定义实现,因为ViewFactory接口决定了中断权重以及中断应该如何发生:

this.jTextPane.setEditorKit(new StyledEditorKit(){
    @Override 
       public ViewFactory getViewFactory(){
           return new LetterWrappingStyledViewFactory(maxCharWidth);
        } 
    }); 
下面是我对ViewFactory界面的实现:


此示例使用用Java7测试的StyledEditorKit

import javax.swing.*;
import javax.swing.text.*;


public class WrapApp extends JFrame {
    JEditorPane edit=new JEditorPane();
    public WrapApp() {
        super("Wrap in the mid");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        edit.setEditorKit(new WrapEditorKit());
        edit.setText("111 222 333333333333333333333333333333333333333333333");

        getContentPane().add(new JScrollPane(edit));
        setSize(200,200);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        WrapApp m = new WrapApp();
        m.setVisible(true);
    }


}

class WrapEditorKit extends StyledEditorKit {
    ViewFactory defaultFactory=new WrapColumnFactory();
    public ViewFactory getViewFactory() {
        return defaultFactory;
    }

}

class WrapColumnFactory implements ViewFactory {
    public View create(Element elem) {
        String kind = elem.getName();
        if (kind != null) {
            if (kind.equals(AbstractDocument.ContentElementName)) {
                return new WrapLabelView(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 IconView(elem);
            }
        }

        // default to text display
        return new LabelView(elem);
    }
}

class WrapLabelView extends LabelView {
    public WrapLabelView(Element elem) {
        super(elem);
    }

    public int getBreakWeight(int axis, float pos, float len) {
        if (axis == View.X_AXIS) {
            checkPainter();
            int p0 = getStartOffset();
            int p1 = getGlyphPainter().getBoundedPosition(this, p0, pos, len);
            if (p1 == p0) {
                // can't even fit a single character
                return View.BadBreakWeight;
            }
            return View.GoodBreakWeight;
        }
        return super.getBreakWeight(axis, pos, len);
    }
    public float getMinimumSpan(int axis) {
        switch (axis) {
            case View.X_AXIS:
                return 0;
            case View.Y_AXIS:
                return super.getMinimumSpan(axis);
            default:
                throw new IllegalArgumentException("Invalid axis: " + axis);
        }
    }

    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);
            GlyphView v = (GlyphView) createFragment(p0, p1);
            return v;
        }
        return super.breakView(axis, p0, pos, len);
    }
}
更新:

关于画家。事实上,我们应该找到一个位置,可以在getBoundedPosition中查看过去。p0表示文档GlyphView中的字符偏移量表示字符元素或元素片段 想象一下一大串文本。在文档中,它只是一个元素,因为它具有所有相同的属性。但是我们必须创建多个LabelView,因为文本片段太大,无法适应可用的宽度。因此,对于第一个标签,p0为0。接下来是一个偏移,我们打破了最初的巨大视野。如果我们再次打破它,就会有另一个偏移量。例如,2个参数仅表示容器父视图的x移位和宽度


因此,我们有一个大的文本,应该找到哪里打破。当前的方式更简单,因为我们不关心空间与其他角色的优先级。

您尝试过吗?谢谢。我已经读过这两本书,我正在努力理解它。我不确定你想要什么的细节,但camickr和StanislavL的预制工具总体上相当不错。如果您在其中一个链接中找到答案,请将其作为副本关闭。如果不是的话,那么deployate.camickr的解决方案实现了一个无换行JTextPane,并具有适当的插入符号可见性,这不是我想要的。我需要在显示边界而不是单词边界进行包装。StanislavL的解决方案不是这样吗?非常感谢。它就像一个初始测试的符咒。我明天还要做进一步的测试。顺便说一句,关于我的场景的更多细节,我首先使用Document.insertString在文档中填充空格,类似于占位符。当字符数据到达时,实际上是VT100协议中的一些串行端口数据,将字符和颜色属性一起插入文档的特定位置。不幸的是,我没有完全明白。你能说点英语吗?特别是p0、pos、len参数的含义以及GlyphPainter的工作方式。一旦S.O.网站允许,我将奖励奖金积分;
import javax.swing.*;
import javax.swing.text.*;


public class WrapApp extends JFrame {
    JEditorPane edit=new JEditorPane();
    public WrapApp() {
        super("Wrap in the mid");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        edit.setEditorKit(new WrapEditorKit());
        edit.setText("111 222 333333333333333333333333333333333333333333333");

        getContentPane().add(new JScrollPane(edit));
        setSize(200,200);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        WrapApp m = new WrapApp();
        m.setVisible(true);
    }


}

class WrapEditorKit extends StyledEditorKit {
    ViewFactory defaultFactory=new WrapColumnFactory();
    public ViewFactory getViewFactory() {
        return defaultFactory;
    }

}

class WrapColumnFactory implements ViewFactory {
    public View create(Element elem) {
        String kind = elem.getName();
        if (kind != null) {
            if (kind.equals(AbstractDocument.ContentElementName)) {
                return new WrapLabelView(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 IconView(elem);
            }
        }

        // default to text display
        return new LabelView(elem);
    }
}

class WrapLabelView extends LabelView {
    public WrapLabelView(Element elem) {
        super(elem);
    }

    public int getBreakWeight(int axis, float pos, float len) {
        if (axis == View.X_AXIS) {
            checkPainter();
            int p0 = getStartOffset();
            int p1 = getGlyphPainter().getBoundedPosition(this, p0, pos, len);
            if (p1 == p0) {
                // can't even fit a single character
                return View.BadBreakWeight;
            }
            return View.GoodBreakWeight;
        }
        return super.getBreakWeight(axis, pos, len);
    }
    public float getMinimumSpan(int axis) {
        switch (axis) {
            case View.X_AXIS:
                return 0;
            case View.Y_AXIS:
                return super.getMinimumSpan(axis);
            default:
                throw new IllegalArgumentException("Invalid axis: " + axis);
        }
    }

    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);
            GlyphView v = (GlyphView) createFragment(p0, p1);
            return v;
        }
        return super.breakView(axis, p0, pos, len);
    }
}