JEditorPane linewrap in Java7

前端 未结 2 1338
醉话见心
醉话见心 2021-01-01 04:01

First of all I hope it\'s not a problem I started a new topic. Tbh I don\'t have a clue how to ask a question based on an already answered one, so I made this.

I\'m

相关标签:
2条回答
  • 2021-01-01 04:18

    A deadly better solution I found : The <br> is correctly handled by the HTMLEditorKit, but the Patrick Sebastien's post mentionned that it won't. It's because its ViewFactory threat all InlineView object as wrappable, but the BRView is also an InlineView. See my solution below:

    class WrapColumnFactory extends HTMLEditorKit.HTMLFactory {
    
            @Override
            public View create(Element elem) {
                View v = super.create(elem);
    
                if (v instanceof LabelView) {
    
                    // the javax.swing.text.html.BRView (representing <br> tag) is a LabelView but must not be handled
                    // by a WrapLabelView. As BRView is private, check the html tag from elem attribute
                    Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
                    if ((o instanceof HTML.Tag) && o == HTML.Tag.BR) {
                        return v;
                    }
    
                    return new WrapLabelView(elem);
                }
    
                return v;
            }
        }
    
        class WrapLabelView extends LabelView {
    
            public WrapLabelView(Element elem) {
                super(elem);
            }
    
            @Override
            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);
                }
            }
    
        }
    
    0 讨论(0)
  • 2021-01-01 04:20

    OK! So, I finally got everything you were having problems with working. It took some research and a lot of trial and error, but here it is:

    Here is what I did:

    • Put the JEditorPane in a JScrollPane so you can scroll up and down as the message gets bigger
    • Added a custom word wrap. The custom word wrap will wrap words and long words in the desired location of the word. You were right, this is a bug with the current version of Java. http://bugs.sun.com/view_bug.do?bug_id=7125737
    • Added the ability for the user to wrap to a new line by hitting Enter. This interfered with the custom word wrap though, so you may not like how I achieved this. In the code example I suggest other options.
    • Preserved your HTMLDocument abilities. I was tempted to not do this, but I found work arounds so that it could be preserved.
    • The application still uses a JEditorPane, but you could switch it to a JTextPane if you want. I tried both and they were both functional.

    So here is the code. It's a bit long and you may wish to change it based on your preferences. I commented where I made changes and tried to explain them.

    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) {
        }
    
    }
    

    Here are (most of) the links that I used to solve this problem:

    Enabling word wrap in a JTextPane with HTMLDocument

    Custom wrap is a combination of these two:

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

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

    Deleting the keybind for JTextArea:

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

    If you have any questions whatsoever, just comment below. I will answer them. I sincerely hope this solves your problems

    0 讨论(0)
提交回复
热议问题