在上一篇 重定向System.out和System.err到JTextPane,分别用黑色红色显示 中讲了如何把 System.out 和 System.err 重定向到 JTextPane 上,并分不同颜色显示,其中用到了 PipedInputStream、PipedOutputStream。那个例子还是参考的 《The Java Developers Almanac 1.4》,翻译出来叫做 《Java 开发者年鉴 1.4》,显得多么的权威啊,我当时还真把它看成官方最佳推荐实现了,太迷信了。
可是现在看来,前面那个实现不仅代码繁琐,而且是 Bug 多多。现在重新对上回的 ConsolePane 来个新的更简洁高效的实现。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
package com.unmi; import java.awt.*; import java.io.*; import javax.swing.*; import javax.swing.text.*; /** * @author Unmi */ public class ConsolePane extends JScrollPane { private JTextPane textPane = new JTextPane(); private static ConsolePane console = null; public static synchronized ConsolePane getInstance() { if (console == null) { console = new ConsolePane(); } return console; } private ConsolePane() { setViewportView(textPane); // Set up System.out PrintStream mySystemOut = new MyPrintStream(System.out, Color.BLACK); System.setOut(mySystemOut); // Set up System.err PrintStream mySystemErr = new MyPrintStream(System.err, Color.RED); System.setErr(mySystemErr); textPane.setEditable(true); setPreferredSize(new Dimension(640, 120)); } /** * Returns the number of lines in the document. */ private final int getLineCount() { return textPane.getDocument().getDefaultRootElement().getElementCount(); } /** * Returns the start offset of the specified line. * @param line The line * @return The start offset of the specified line, or -1 if the line is * invalid */ private int getLineStartOffset(int line) { Element lineElement = textPane.getDocument().getDefaultRootElement() .getElement(line); if (lineElement == null) return -1; else return lineElement.getStartOffset(); } /** * 清除超过行数时前面多出行的字符 */ private void replaceRange(String str, int start, int end) { if (end < start) { throw new IllegalArgumentException("end before start"); } Document doc = textPane.getDocument(); if (doc != null) { try { if (doc instanceof AbstractDocument) { ((AbstractDocument) doc).replace(start, end - start, str, null); } else { doc.remove(start, end - start); doc.insertString(start, str, null); } } catch (BadLocationException e) { throw new IllegalArgumentException(e.getMessage()); } } } class MyPrintStream extends PrintStream { private Color foreground; //输出时所用字体颜色 /** * 构造自己的 PrintStream * @param out 可传入 System.out 或 System.err, 实际不起作用 * @param foreground 显示字体颜色 */ MyPrintStream(OutputStream out,Color foreground) { super(out,true); //使用自动刷新 this.foreground = foreground; } /** * 在这里重截,所有的打印方法都要调用最底一层的方法 */ public void write(byte[] buf, int off, int len) { final String message = new String(buf, off, len); /** SWING非界面线程访问组件的方式 */ SwingUtilities.invokeLater(new Runnable() { public void run() { try { StyledDocument doc = (StyledDocument) textPane .getDocument(); // Create a style object and then set the style // attributes Style style = doc.addStyle("StyleName", null); // Foreground color StyleConstants.setForeground(style, foreground); doc.insertString(doc.getLength(), message, style); } catch (BadLocationException e) { // e.printStackTrace(); } // Make sure the last line is always visible textPane.setCaretPosition(textPane.getDocument() .getLength()); // Keep the text area down to a certain line count int idealLine = 150; int maxExcess = 50; int excess = getLineCount() - idealLine; if (excess >= maxExcess) { replaceRange("", 0, getLineStartOffset(excess)); } } }); } } } |
使用方法依旧,是一个 JScrollPane,加上自己的面板上就行:
getContentPane().add(ConsolePane.getInstance(), BorderLayout.CENTER);
界面效果图同前面基本一样:
能解决的问题恰恰就是上面遗留下来的1、2、3:
1. 不再产生 java.io.IOException: Write end dead 异常
2. 输出时不再会缺几个字母,或产生空行了
3. 输出顺序根据程序执行先后能得到保证
如果希望能捕获到 Log4j 的输出,仍然依赖于要用 Log4j 1.2.13 或以上的版本,并设置属性:
log4j.appender.console.follow = true
这个问题在上一篇 重定向System.out和System.err到JTextPane,分别用黑色红色显示 Log4J 与 ConsolePane 一节中用讲,但是如果是用 SWT 的话,这个问题可以解决,不再依赖于这一属性设置了。
这段时间正在研究 SWT,感觉用起来比 SWING 舒服多了,考虑暂时放耽下 SWING 了,过阵会再写一篇关于把 System.out 和 System.err 定向到 SWT 文本控件的日志。
TestConsolePane 代码还是请见上篇日志:重定向System.out和System.err到JTextPane,分别用黑色红色显示
不错,学习了~