在JTextPane中为文本着色的有效方法

时间:2015-06-20 00:03:00

标签: java swing colors jtextpane

我有一个关于在JTextPane中着色某些关键字的问题。换句话说,我想制作类似迷你IDE的东西,所以我会编写一些代码,我想为某些关键字提供一种颜色(比如蓝色),例如" public" "私人" ......等问题是它非常慢!!因为每次我都会进入"空间"或"退格" key函数扫描整个文本以给关键字一个颜色,所以当我在textpane中编写大量代码时,它变得非常慢。 这是我匹配关键字的功能:

public void matchWord() throws BadLocationException {
        String tokens[] = ArabicParser.tokenNames;
        int index = 0;
        String textStr[] = textPane.getText().split("\\r?\\n");
        for(int i=0 ; i<textStr.length ; i++) {
            String t = textStr[i];
            StringTokenizer ts2 = new StringTokenizer(t, " ");
            while(ts2.hasMoreTokens()) {
                String token = ts2.nextToken();

                // The iterations are reduced by removing 16 symbols from the search space
                for(int j = 3 ; j<tokens.length-5 ; j++) {
                    if(!(token.equals("؛")) && (tokens[j].equals("'"+token+"'"))) {
                        changeColor(textPane,token,Color.BLUE,index,token.length());
                        break;
                    } else {
                        changeColor(textPane,token,Color.BLACK,index,token.length());
                    }
                }
                index += token.length() + 1;
            }
            //index -= 1;
        }
    }

这是我为匹配的单词着色的功能:

private void changeColor(JTextPane tp, String msg, Color c, int beginIndex, int length) throws BadLocationException {
        SimpleAttributeSet sas = new SimpleAttributeSet(); 
        StyleConstants.setForeground(sas, c);
        StyledDocument doc = (StyledDocument)tp.getDocument();
        doc.setCharacterAttributes(beginIndex, length, sas, false);
        sas = new SimpleAttributeSet(); 
        StyleConstants.setForeground(sas, Color.BLACK);
        tp.setCharacterAttributes(sas, false);
    }

并提前感谢=)

3 个答案:

答案 0 :(得分:1)

考虑替换StringTokenizer,因为现代使用已被删除https://stackoverflow.com/a/6983908/1493294

考虑将String tokens[]重构为HashSet<String> tokens。散列查找比循环更快,特别是当tokens[]变大时。

如果您想使用两种以上的颜色,请尝试HashMap<String, Color> tokens

另外,在这里遇到两个非常不同的叫做tokentokens的东西令人困惑。考虑将tokens[]重命名为coloredNames[],使其明显不同于token令牌中的textPane

考虑使用分析器来查看大部分时间花在哪里。您可能会发现changeColor()中的重复性工作值得缓存。

如果是这样,请写一个名为ColorChanger的类。 ColorChanger将有一个构造函数和一个方法changeColor()。构造函数将采用(并因此缓存)在循环时不会更改的参数。 ColorChanger.changeColor()将采用在循环时更改的参数。

答案 1 :(得分:1)

您可以使用DocumentListener仅分析插入TextPane中的文本。这样,您就不需要多次分析整个文本,只检查添加的内容。

为此,您需要获取javax.swing.text.Utilities类的getWordStartgetWordEnd方法。这样,您就可以获得插入位置的周围环境。

修改:删除可以更改关键字的状态。删除后,您需要在删除开始位置和getWordStart之间以及删除结束位置和getWordEnd之间的文本之间获取文本。例如,如果你删除&#34; continental sur&#34;来自&#34;洲际表面&#34;,你会得到&#34;界面&#34;这可能是一个关键字。

例如,您可以使用此类:

import javax.swing.text.Utilities;
public class Highlighter implements DocumentListener {

    public void insertUpdate(final DocumentEvent e) {
        highlight(e.getDocument(),e.getOffset(),e.getLength());
    }

    public void removeUpdate(DocumentEvent e) {
        highlight(e.getDocument(), e.getOffset(), 0);
    }

    public void changedUpdate(DocumentEvent e) {}

    private void highlight(final Document doc, final int offset, final int length) {
        //Edit the color only when the EDT is ready
        SwingUtilities.invokeLater(new Runnable() 
            public void run() {
                //The impacted text is the edition + the surrounding part words.
                int start = Utilities.getWordStart(myJTextPane,offset);
                int end = Utilities.getWordEnd(myJTextPane,offset+length);
                String impactedText = doc.getText(start,end-start);
                applyHighlighting(doc, impactedText, offset);
            }
        });
    }

    private void applyHighlighting(Document doc, String text, int offset) {
        //we review each word and color them if needed.
        StringTokenizer tokenizer = new StringTokenizer(text, " \t\n\r\f,.:;?![]'()");
        int start = 0;
        while(tokenizer.hasMoreTokens()) {
            String word = tokenizer.nextToken();
            start = text.indexOf(word,start+1);
            if(isKeyword(word)) {
                //you can use the method you proposed for instance as a start.
                changeColor(myJTextPane, word, Color.BLUE, start, word.length());
            } else if(offset==0 || !tokenizer.hasMoreTokens()) {
                //The first and last word's state can have changed. 
                //We need to put them back in BLACK if needed.
                changeColor(myJTextPane, word, Color.BLACK, start, word.length());
            }
        }
    }
}

答案 2 :(得分:1)

  

问题是它非常慢!!因为每次我都会进入&#34;空间&#34;或&#34;退格&#34;键功能扫描全文

通过仅处理更改的行,您可以提高效率。

当文档发生变化时,// Start output buffering; it redirects any generated content to a memory buffer ob_start(); // Include the desired file; this executes the PHP code it contains // but because of the output buffering, the HTML code is not displayed // here but buffered include 'myfile.phtml'; // Get the content of the buffer, clear the buffer, end the buffering $text = ob_get_clean(); // // ... more code and/or HTML follows // // When you need the content of 'myfile.phtml' you just: echo($text); // // ... more code and/or HTML follows // // If you need to display the content of 'myfile.phtml' again you just: echo($mytext); 可用于通知您。然后,您只能解析受更改影响的行。请记住,可以将多行文本粘贴到文本窗格中,因此您需要处理这种情况。

以下是DocumentListener的简单结构的一些(未经测试的)代码,您可以使用它来处理更改的行:

DocumentListener

需要public class KeywordDocumentListener implements DocumentListener { public void insertUpdate(final DocumentEvent e) { SwingUtilities.invokeLater(new Runnable() { public void run() { processChangedLines(e.getDocument(), e.getOffset(), e.getLength()); } }); } public void removeUpdate(DocumentEvent e) { SwingUtilities.invokeLater(new Runnable() { public void run() { processChangedLines(e.getDocument(), e.getOffset(), 0); } }); } public void changedUpdate(DocumentEvent e) {} private void processChangedLines(Document doc, int offset, int length) { // The lines affected by the latest document update Element rootElement = doc.getDefaultRootElement(); int startLine = rootElement.getElementIndex(offset); int endLine = rootElement.getElementIndex(offset + length); // Do the highlighting one line at a time for (int i = startLine; i <= endLine; i++) { int lineStart = rootElement.getElement( i ).getStartOffset(); int lineEnd = rootElement.getElement( i ).getEndOffset() - 1; String lineText = doc.getText(lineStart, lineEnd - lineStart); applyHighlighting(doc, lineText, lineStart); } } private void applyHighlighting(Document doc, String text, int lineStart) { // Now you can search a line of text for your keywords // As you find a keyword to highlight you add the lineStart to the search // location so the highlight is the proper offset in the Document } } ,因为您无法在DocumentListener中更新Document,因此这会将代码放在EDT的末尾,以便在侦听器执行完毕后执行。

对于简单的解析,我没有看到使用StringTokeninzer的问题。它比使用正则表达式更有效。

  

为关键字指定颜色

实际上,除了着色关键字之外,您还要着色每个普通单词,这些单词效率不高。我建议您将整行文本设置为BLACK前景色。然后作为您的解析,您只会突出显示您使用蓝色颜色找到的标记。这将显着减少对Document执行的属性更改次数。

不要为每个令牌创建新的AttributeSet。创建一次AttributeSet,然后为每个令牌重复使用它。