当前位置: 首页 > 面试题库 >

Java7中的JEditorPane换行

宗政财
2023-03-14
问题内容

首先,我希望这不是问题,所以我开始了一个新话题。我不知道如何根据已经回答的问题提出问题,所以我做到了。

我对Java很陌生,以下是我的问题。我正在编写一个小型聊天程序,并且使用JEditorPane带有的HTMLEditorKit来显示不同颜色的文本,显示笑脸和显示超链接。

我的问题是,经过一些研究,我发现问题可能是由于Java7造成的,我无法使换行器正常工作。我希望文本自动换行,并包裹在超出组件宽度的字符串中间。自动换行可以很好地工作,但是如果有人输入一个很长的字符串,它JEditorPane会被扩展,并且您需要调整框架的大小以将所有内容显示在屏幕上,这是我不想发生的事情。

我已经尝试了一些解决此问题的方法,但是它们只允许换行,这样换行就不再起作用了。除此之外,我希望用户能够通过按Enter键来包装其文本。为此,我要在文本中添加\
n并进行修复,这将不再影响结果,并且所有内容都将显示在一行中。

我感觉自己已经花了很多年时间在网络上寻找解决方案,但是unitl现在对我的情况无济于事,特别是因为它似乎一直都是相同的解决方案。我希望你们能帮助我。

总之,这意味着:

我有的:

  • 如果长字符串之间用空格分隔,则换行
  • 如果您使用Windows,并且您的输入包含按回车键创建的换行符,则它们还将换行
  • 如果您输入的字符串很长且没有空格,则面板会扩展,您需要调整框架的大小
  • HTML格式允许我显示不同的颜色以及超链接和表情

我需要的:

  • 如果可能的话,请使用自动换行功能,但只有在长字符串之间没有空格分隔时才能自动换行,以防止面板扩展。
  • 手动添加换行,方法是在输入区域中按ENTER键,或者将预格式化的文本复制到输入面板中
  • 像我一样的HTML格式

我尝试过的和没有帮助的:

jtextpane不换行,
JTextPane不换行

这是一些代码,您可以自己尝试。左下方是一个输入区域,用于输入一些文本。您还可以通过按Enter键添加换行。单击按钮后,您将在上方区域中看到文本。

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.border.TitledBorder;
import javax.swing.text.BadLocationException;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;

@SuppressWarnings("serial")
public class LineWrapTest extends JFrame implements ActionListener, KeyListener {

private JButton btnSend;
private JTextArea textAreaIn;
private JEditorPane textAreaOut;
private HTMLEditorKit kit;
private HTMLDocument doc;

public LineWrapTest() {

    this.setSize(600, 500);
    this.setDefaultCloseOperation(EXIT_ON_CLOSE);
    this.setLocationRelativeTo(null);
    this.setTitle("Linewrap Test");
}

/**
 * Not important for problem
 */
public void paintScreen() {

    this.setLayout(new BorderLayout());

    this.add(this.getPanelOut(), BorderLayout.CENTER);
    this.add(this.getPanelIn(), BorderLayout.SOUTH);

    this.textAreaIn.requestFocusInWindow();
    this.setVisible(true);
}

/**
 * Not important for problem
 * 
 * @return panelOut
 */
private JPanel getPanelOut() {

    JPanel panelOut = new JPanel();
    panelOut.setLayout(new BorderLayout());

    this.textAreaOut = new JEditorPane();
    this.textAreaOut.setEditable(false);
    this.textAreaOut.setContentType("text/html");

    this.kit = new HTMLEditorKit();
    this.doc = new HTMLDocument();

    StyleSheet styleSheet = this.kit.getStyleSheet();
    this.kit.setStyleSheet(styleSheet);

    this.textAreaOut.setEditorKit(this.kit);
    this.textAreaOut.setDocument(this.doc);

    TitledBorder border = BorderFactory.createTitledBorder("Output");
    border.setTitleJustification(TitledBorder.CENTER);

    panelOut.setBorder(border);
    panelOut.add(this.textAreaOut);

    return panelOut;
}

/**
 * Not important for problem
 * 
 * @return panelIn
 */
private JPanel getPanelIn() {

    JPanel panelIn = new JPanel();
    panelIn.setLayout(new BorderLayout());

    this.textAreaIn = new JTextArea();
    this.textAreaIn.setLineWrap(true);
    this.textAreaIn.setWrapStyleWord(true);

    TitledBorder border = BorderFactory.createTitledBorder("Input");
    border.setTitleJustification(TitledBorder.CENTER);

    panelIn.setBorder(border);
    panelIn.add(this.getBtnSend(), BorderLayout.EAST);
    panelIn.add(this.textAreaIn, BorderLayout.CENTER);

    return panelIn;
}

/**
 * Not important for problem
 * 
 * @return btnSend
 */
private JButton getBtnSend() {

    this.btnSend = new JButton("Send");
    this.btnSend.addActionListener(this);

    return this.btnSend;
}

private void append(String text) {

    try {
        this.kit.insertHTML(this.doc, this.doc.getLength(), text, 0, 0, null);
    } catch (BadLocationException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private String getHTMLText() {

    String txtIn = this.textAreaIn.getText().trim().replaceAll(SEPARATOR, "<br/>");

    StringBuffer htmlBuilder = new StringBuffer();

    htmlBuilder.append("<HTML>");
    htmlBuilder.append(txtIn);
    htmlBuilder.append("</HTML>");

    return htmlBuilder.toString();
}

@Override
public void actionPerformed(ActionEvent e) {

    if (e.getSource() == this.btnSend) {
        this.append(this.getHTMLText());
        this.textAreaIn.setText("");
        this.textAreaIn.requestFocusInWindow();
    }
}

public static void main(String[] args) {

    LineWrapTest test = new LineWrapTest();
    test.paintScreen();
}

@Override
public void keyPressed(KeyEvent e) {

    if (e.getKeyCode() == KeyEvent.VK_ENTER)
        if (!this.textAreaIn.getText().trim().isEmpty())
            this.textAreaIn.setText(this.textAreaIn.getText() + SEPARATOR);
}

@Override
public void keyReleased(KeyEvent e) {
}

@Override
public void keyTyped(KeyEvent e) {
}
}

更新: 基于http://java-
sl.com/tip_java7_text_wrapping_bug_fix.html的某些部分

不知何故,我想出了一点办法可以更接近我的目标。我尝试将HTMLEditorKit的修复程序与StlyedEditorKit修复程序结合在一起。但老实说,我不知道我在那实际上做了什么:(可悲的是,手动换行不再可以代替HTMLEditorKit来使用。也许您可以将其用作更好地实施的基础。

要在我的示例中使用它,只需使用CustomEditorKit在项目中创建一个新类,然后使用此CustomEditorKit替换示例中的HTMLEditorKit。您会注意到单词和字母换行现在可以使用,但是如果您按ENTER键来进行自己的换行,则此更改将不再出现在输出面板中,并且所有内容都将显示在一行中。另一个奇怪的问题是,如果您调整框架的大小,线条有时会相互重叠。

import javax.swing.SizeRequirements;
import javax.swing.text.Element;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.InlineView;
import javax.swing.text.html.ParagraphView;

@SuppressWarnings("serial")
public class CustomEditorKit extends HTMLEditorKit {

@Override
public ViewFactory getViewFactory() {

    return new HTMLFactory() {
        @Override
        public View create(Element e) {
            View v = super.create(e);
            if (v instanceof InlineView) {
                return new InlineView(e) {
                    @Override
                    public int getBreakWeight(int axis, float pos, float len) {
                        return GoodBreakWeight;
                    }

                    @Override
                    public View breakView(int axis, int p0, float pos, float len) {
                        if (axis == View.X_AXIS) {
                            this.checkPainter();
                            this.removeUpdate(null, null, null);
                        }
                        return super.breakView(axis, p0, pos, len);
                    }
                };
            }
            else if (v instanceof ParagraphView) {
                return new ParagraphView(e) {
                    @Override
                    protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
                        if (r == null) {
                            r = new SizeRequirements();
                        }
                        float pref = this.layoutPool.getPreferredSpan(axis);
                        float min = this.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;
                    }

                };
            }
            return v;
        }
    };
    }
}

问题答案:

好!因此,我终于得到了您工作中遇到的所有问题。经过了一些研究和大量的反复试验,但是这里是:

这是我所做的:

  • 将JEditorPane放在JScrollPane中,以便在消息变大时可以上下滚动
  • 添加了自定义自动换行。自定义自动换行会将单词和长单词包装在所需的单词位置。您说对了,这是当前Java版本的错误。http://bugs.sun.com/view_bug.do?bug_id=7125737
  • 通过单击Enter,使用户可以换行。但是,这会干扰自定义自动换行,因此您可能不喜欢我的实现方式。在代码示例中,我建议其他选项。
  • 保留您的HTMLDocument功能。我很想不这样做,但是我发现周围的环境可以保留它。
  • 该应用程序仍使用JEditorPane,但如果需要,可以将其切换为JTextPane。我都尝试过,它们都起作用。

所以这是代码。它有点长,您可能希望根据自己的喜好进行更改。我评论了所做的更改,并试图解释它们。

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.SizeRequirements;
import javax.swing.border.TitledBorder;
import javax.swing.text.*;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.InlineView;
import javax.swing.text.html.StyleSheet;

@SuppressWarnings("serial")
public class LineWrapTest extends JFrame implements ActionListener, KeyListener {

    //This is the separator.
    private String SEPARATOR = System.getProperty("line.separator");
    private JButton btnSend;
    private JTextArea textAreaIn;
    private JEditorPane textAreaOut;
    private JScrollPane outputScrollPane;
    private HTMLEditorKit kit;
    private HTMLDocument doc;


    public LineWrapTest() {

        this.setSize(600, 500);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
        this.setTitle("Linewrap Test");
    }

    /**
     * Not important for problem
     */
    public void paintScreen() {

        this.setLayout(new BorderLayout());

        this.add(this.getPanelOut(), BorderLayout.CENTER);
        this.add(this.getPanelIn(), BorderLayout.SOUTH);

        this.textAreaIn.requestFocusInWindow();
        this.setVisible(true);
    }


    /**
     * Not important for problem
     * 
     * @return panelOut
     */
    private JPanel getPanelOut() {

        JPanel panelOut = new JPanel();
        panelOut.setLayout(new BorderLayout());

        this.textAreaOut = new JEditorPane();
        this.textAreaOut.setEditable(false);
        this.textAreaOut.setContentType("text/html");

        //I added this scroll pane.
        this.outputScrollPane = new JScrollPane(this.textAreaOut);

        /*
         * This is a whole whack of code.  It's a combination of two sources.
         * It achieves the wrapping you desire: by word and longgg strings
         * It is a custom addition to HTMLEditorKit
         */
        this.kit = new HTMLEditorKit(){
           @Override 
           public ViewFactory getViewFactory(){

               return new 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;
                                  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;
                                      }
                                      try {
                                          //if the view contains line break char return forced break
                                          if (getDocument().getText(p0, p1 - p0).indexOf(SEPARATOR) >= 0) {
                                              return View.ForcedBreakWeight;
                                          }
                                      }
                                      catch (BadLocationException ex) {
                                          //should never happen
                                      }

                                  }
                                  return super.getBreakWeight(axis, pos, len);
                              } 
                              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);
                                      try {
                                          //if the view contains line break char break the view
                                          int index = getDocument().getText(p0, p1 - p0).indexOf(SEPARATOR);
                                          if (index >= 0) {
                                              GlyphView v = (GlyphView) createFragment(p0, p0 + index + 1);
                                              return v;
                                          }
                                      }
                                      catch (BadLocationException ex) {
                                          //should never happen
                                      }

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

                            }; 
                        } 
                      return v; 
                    } 
                }; 
            } 
        };

        this.doc = new HTMLDocument();

        StyleSheet styleSheet = this.kit.getStyleSheet();
        this.kit.setStyleSheet(styleSheet);

        this.textAreaOut.setEditorKit(this.kit);
        this.textAreaOut.setDocument(this.doc);

        TitledBorder border = BorderFactory.createTitledBorder("Output");
        border.setTitleJustification(TitledBorder.CENTER);

        panelOut.setBorder(border);

        //I changed this to add the scrollpane, which now contains
        //the JEditorPane
        panelOut.add(this.outputScrollPane);

        return panelOut;
    }

    /**
     * Not important for problem
     * 
     * @return panelIn
     */
    private JPanel getPanelIn() {

        JPanel panelIn = new JPanel();
        panelIn.setLayout(new BorderLayout());

        this.textAreaIn = new JTextArea();
        this.textAreaIn.setLineWrap(true);
        this.textAreaIn.setWrapStyleWord(true);

        //This disables enter from going to a new line.  Your key listener does that.
        this.textAreaIn.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "none");
        //For the key listener to work, it needs to be added to the component
        this.textAreaIn.addKeyListener(this);

        TitledBorder border = BorderFactory.createTitledBorder("Input");
        border.setTitleJustification(TitledBorder.CENTER);

        panelIn.setBorder(border);
        panelIn.add(this.getBtnSend(), BorderLayout.EAST);
        panelIn.add(this.textAreaIn, BorderLayout.CENTER);

        return panelIn;
    }

    /**
     * Not important for problem
     * 
     * @return btnSend
     */
    private JButton getBtnSend() {

        this.btnSend = new JButton("Send");
        this.btnSend.addActionListener(this);

        return this.btnSend;
    }


    private void append(String text) {

        try {
            this.kit.insertHTML(this.doc, this.doc.getLength(), text, 0, 0, null);
        } catch (BadLocationException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String getHTMLText() {
        //I tried to find a work around for this but I couldn't.  It could be done
        //by manipulating the HTMLDocument but it's beyond me.  Notice I changed
        //<br/> to <p/>.  For some reason, <br/> no longer went to the next line
        //when I added the custom wrap.  <p/> seems to work though.
        String txtIn = this.textAreaIn.getText().trim().replaceAll(SEPARATOR, "<p/>");

        //My IDE recommends you use StringBuilder instead, that's up to you.
        //I am not sure what the difference would be.
        StringBuffer htmlBuilder = new StringBuffer();

        htmlBuilder.append("<HTML>");
        htmlBuilder.append(txtIn);
        htmlBuilder.append("</HTML>");

        return htmlBuilder.toString();
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        if (e.getSource() == this.btnSend) {
            this.append(this.getHTMLText());
            this.textAreaIn.setText("");
            this.textAreaIn.requestFocusInWindow();
        }
    }

    public static void main(String[] args) {
        LineWrapTest test = new LineWrapTest();
        test.paintScreen();
    }

    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_ENTER){
            if (!this.textAreaIn.getText().trim().isEmpty()) {
                //I made this work by defining the SEPARATOR.
                //You could use append(Separator) instead if you want.
                this.textAreaIn.setText(this.textAreaIn.getText() + SEPARATOR);
            }
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

}

这里是(大多数)我用来解决此问题的链接:

使用HTMLDocument在JTextPane中启用自动换行

自定义换行是以下两者的组合:

http://java-sl.com/tip_html_letter_wrap.html

http://java-sl.com/wrap.html

删除JTextArea的keybind:

http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html

如果您有任何疑问,请在下面评论。 我会回答他们。我衷心希望这能解决您的问题



 类似资料:
  • 主要内容:1 Java JEditorPane的介绍,2 Java JEditorPane的内部类,3 Java JEditorPane的字段,4 Java JEditorPane的构造方法,5 Java JEditorPane的方法,6 Java JEditorPane的案例1,7 Java JEditorPane的案例21 Java JEditorPane的介绍 JEdi​​torPane类用于创建一个简单的文本编辑器窗口。此类具有setContentType() 和setText() 方法

  • 问题内容: 我已经成功使用httpclient成功登录到站点,并打印出启用该登录的cookie。但是,我现在陷入困境,因为我想使用.setPage(url)函数在JEditorPane中显示后续页面。但是,当我这样做并使用Wireshark分析我的GET请求时,我看到用户代理不是我的httpclient而是以下内容: 用户代理:Java / 1.6.0_17 GET请求(在侧面jeditorpan

  • 问题内容: 我正在尝试为项目创建一个简单的Web浏览器,但是我正在使用JEditorPane呈现和显示HTML。我听说不应将JEditorPane用作浏览器组件,因为它不支持HTML3.2之上的任何功能(即使那样也无法正确显示它),但是我似乎找不到HTML5的替代方法。谁能帮我?提前致谢。 问题答案: 您可以使用JavaFX-2 Webview。从JavaFX-2 Adding HTML Cont

  • 问题内容: 我正在开发一个Swing应用程序,其中必须显示HTML文件。 我正在为此使用控件。它显示的是HTML文件的内容,但格式与实际HTML文件中的原始格式不同。 不支持该元素。与其加载目标文件,不如显示它。 Swing中是否还有其他内置控件来浏览HTML文件而不是? 我正在使用以下代码: 问题答案: 在核心Java中进行更高级的HTML呈现的最佳选择是使用基于JavaFX的组件,如向Java

  • 问题内容: 我正在尝试在Java JEditorPane中显示嵌入式图像。下面的代码使用HTML内容,这些内容可以在Firefox中正确显示图像,但不能在JEditorPane中显示图像。有什么想法吗?谢谢。 问题答案: 您需要为“数据:”添加协议处理程序,以便可以为其打开URL / URLConnection。或者,您可以为类路径资源创建一些协议处理程序“ resource:”。 您需要一个带有

  • 问题内容: 我在JScrollPane中有一个JeditorPane。在应用程序中的某些点,我想检索在scrollPane中可见的文本(当前正在显示的文本)以及仅此文本。有没有办法做到这一点? 谢谢, 艾略特 问题答案: 您可以使用视口来获取视图的位置和大小。 一旦知道了视口的起点/终点,就可以使用: 一旦知道了文本的偏移量,就可以从组件中获取文本: 没有代码经过测试。