4

まず第一に、それが問題ではないことを願っています 新しいトピックを開始しました。すでに回答された質問に基づいて質問する方法がわからないので、これを作成しました。

私はJavaにかなり慣れていませんが、私の問題は次のとおりです。私は小さなチャット プログラムを作成しており、 を使用してJEditorPaneテキストHTMLEditorKitをさまざまな色で表示したり、スマイリーを表示したり、ハイパーリンクを表示したりしています。

私の問題は、いくつかの調査の結果、問題がJava7に起因する可能性があることが判明したため、ラインラップを適切に機能させることができないことです。テキストをワードラップし、コンポーネントの幅を超える文字列の途中でラップしたい。ワードラップは正常に機能しますが、誰かがかなり長い文字列を入力するJEditorPaneと展開され、フレームのサイズを変更してすべてを画面に表示する必要があります。これは私が起こりたくないことです.

この問題に対していくつかの修正を試みましたが、文字の折り返しのみが許可され、単語の折り返しが機能しなくなりました。それに加えて、ユーザーが Enter キーを押してテキストを折り返すことができるようにしたいと考えています。そのために、テキストに \n を追加しています。修正により、これは結果に影響を与えなくなり、すべてが 1 行で表示されるようになります。

解決策を見つけるために何年もウェブで過ごしたような気がしますが、特に常に同じ修正であるように見えたため、私の場合は何もうまくいきませんでした。皆さんが私を助けてくれることを願っています。

これは要約すると次のことを意味します。

私が持っているもの:

  • スペースで区切られた長い文字列の場合、改行で単語が折り返されます
  • Windows を使用していて、Enter キーを押すことによって作成された改行が入力に含まれている場合、それらも改行されます。
  • スペースなしで非常に長い文字列を入力すると、パネルが展開され、フレームのサイズを変更する必要があります
  • 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 を使用してプロジェクトに新しいクラスを作成し、例の HTMLEditorKit をこの CustomEditorKit に置き換えます。単語と文字の折り返しが機能するようになりましたが、Enter キーを押して独自の行折り返しを取得すると、この変更は出力パネルに表示されなくなり、すべてが 1 行に表示されます。もう 1 つの奇妙な問題は、フレームのサイズを変更すると、線が重なり合うことがあることです。

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;
        }
    };
    }
}
4

2 に答える 2

6

わかった!それで、私はついにあなたが仕事で問題を抱えていたすべてのものを手に入れました。いくつかの調査と多くの試行錯誤が必要でしたが、次のとおりです。

これが私がしたことです:

  • JEdi​​torPane を 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 でワード ラップを有効にする

カスタム ラップは、次の 2 つの組み合わせです。

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

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

JTextArea のキーバインドの削除:

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

ご不明な点がございましたら、以下にコメントしてください。私はそれらに答えます。これで問題が解決することを心から願っています

于 2013-07-08T19:58:08.493 に答える