【问题标题】:Automatically Coloring the Java Keywords when i am open a file in JTextpane当我在 JTextpane 中打开文件时自动为 Java 关键字着色
【发布时间】:2014-09-21 18:54:50
【问题描述】:

我想将颜色应用于 java 关键字。当我打开一个文件时,它会引发尝试在通知异常中变异并且程序未完全显示并且颜色也未应用。 这是我的代码,

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FileDialog;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

public class JavaKeywordsColor extends javax.swing.JFrame {

    private JTextPane textPane;
    private Color color = Color.BLACK;
    private int i = 0;
    private JPanel noWrapPanel;
    private JScrollPane scrollpane;
    private JTextField status;
    private StyledDocument d;
    private String[] keywords = {"import ", "class ", "int ", "public ", "private"};

    public JavaKeywordsColor() {
        initComponents();
    }

    private void initComponents() {
        tp = new javax.swing.JTabbedPane();
        jMenuBar1 = new javax.swing.JMenuBar();
        jMenu1 = new javax.swing.JMenu();
        Open = new javax.swing.JMenuItem();
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        jMenu1.setText("File");
        Open.setText("Open");
        Open.addActionListener(new java.awt.event.ActionListener() {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                OpenActionPerformed(evt);
            }
        });
        jMenu1.add(Open);
        jMenuBar1.add(jMenu1);
        setJMenuBar(jMenuBar1);
        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(tp, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE));
        layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(tp, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 279, Short.MAX_VALUE));
        pack();
    }// </editor-fold>                        

    private void OpenActionPerformed(java.awt.event.ActionEvent evt) {
        FileDialog fd = new FileDialog(JavaKeywordsColor.this, "Select File", FileDialog.LOAD);
        fd.setVisible(true);
        String title;
        String sts;
        if (fd.getFile() != null) {
            sts = fd.getDirectory() + fd.getFile();
            title = fd.getFile();
            BufferedReader br = null;
            StringBuffer str = new StringBuffer("");
            try {
                br = new BufferedReader(new FileReader(sts));
                String line;
                try {
                    while ((line = br.readLine()) != null) {
                        str.append(line + "\n");
                    }
                } catch (IOException ex) {
                    Logger.getLogger(JavaKeywordsColor.class.getName()).log(Level.SEVERE, null, ex);
                }
            } catch (FileNotFoundException ex) {
                Logger.getLogger(JavaKeywordsColor.class.getName()).log(Level.SEVERE, null, ex);
            }
            String t = str.toString();
            final JInternalFrame internalFrame = new JInternalFrame("", true, true);
            textPane = new JTextPane();
            // textPane.setEditorKit(new WrapEditorKit());
            d = textPane.getStyledDocument();
            d.addDocumentListener(new DocumentListener() {
                @Override
                public void insertUpdate(DocumentEvent de) {
                    jColorActionPerformed(de);
                }

                @Override
                public void removeUpdate(DocumentEvent de) {
                    jColorActionPerformed(de);
                }

                @Override
                public void changedUpdate(DocumentEvent de) {
                    jColorActionPerformed(de);
                }
            });
            Document doc = textPane.getDocument();
            textPane.setFont(new java.awt.Font("Miriam Fixed", 0, 13));
            internalFrame.add(textPane);
            i += 1;
            internalFrame.setName("Doc" + i);
            this.add(new javax.swing.JScrollPane(textPane));
            noWrapPanel = new JPanel(new BorderLayout());
            noWrapPanel.add(textPane);
            scrollpane = new JScrollPane(noWrapPanel);
            internalFrame.add(scrollpane);
            internalFrame.setTitle(title);
            tp.add(internalFrame);
            tp.setSelectedIndex(i - 1);
            internalFrame.setVisible(true);
            textPane.setText(t);
        }
    }

    private void jColorActionPerformed(DocumentEvent evt) {
        replace(textPane);
    }

    public void replace(javax.swing.JTextPane jp) {
        int cur = jp.getCaretPosition();
        StyleContext sc = StyleContext.getDefaultStyleContext();
        AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.BLUE);
        for (int ii = 0; ii < keywords.length; ii++) {
            int fromIndex = 0;
            String msg = keywords[ii];
            int nol = 0;     //number of lines upto keyword
            int len = 1;
            while (len != -1) {
                len = jp.getText().indexOf(msg, fromIndex);
                jp.setSelectedTextColor(Color.RED);
                if (len != -1) {
                    try {
                        nol = countLine(jp.getText(0, len + 1));
                    } catch (BadLocationException ex) {
                        break;
                    }
                    fromIndex = len + msg.length();
                    System.out.println("len = " + len + " nol=" + nol);
                    len -= nol;
                    jp.select(len, len + msg.length());
                    System.out.println("Selected Text = " + jp.getSelectedText());
                    jp.replaceSelection("");
                    jp.setCaretPosition(len);
                    jp.setCharacterAttributes(aset, false);
                    jp.replaceSelection(msg);
                }
            }
        }
        aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.BLACK);
        jp.setCharacterAttributes(aset, false);
        jp.setCaretPosition(cur);
    }

    public int countLine(String str) {
        int n = 0;
        for (int ii = 0; ii < str.length(); ii++) {
            if (str.charAt(ii) == '\n') {
                n++;
            }
        }
        return n;
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new JavaKeywordsColor().setVisible(true);
            }
        });
    }
    private javax.swing.JMenuItem Open;
    private javax.swing.JMenu jMenu1;
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JTabbedPane tp;
}

【问题讨论】:

    标签: java swing colors keyword jtextpane


    【解决方案1】:

    异常的消息很清楚。当侦听器通知正在进行时,您不能更改文档。原因很清楚:修改会触发新的通知,并且在像您这样的程序中,会引发StackOverflowError 或无限循环。这也意味着在其他听众尚未了解旧修改时,可能会报告新修改。

    您必须将修改安排在以后进行,并保护您的代码不会对您自己的修改做出反应。将更新安排到稍后时间也有助于提高用户快速输入时的性能,因为您不想在每个插入的字符上解析整个文档。

    所以你应该替换你添加DocumentListener的部分

    d.addDocumentListener(new DocumentListener() {
        @Override
        public void insertUpdate(DocumentEvent de) {
            jColorActionPerformed(de);
        }
    
        @Override
        public void removeUpdate(DocumentEvent de) {
            jColorActionPerformed(de);
        }
    
        @Override
        public void changedUpdate(DocumentEvent de) {
            jColorActionPerformed(de);
        }
    });
    

    到这样的事情:

    final class UpdateTrigger implements DocumentListener, ActionListener {
      final Timer timer=new Timer(150, this);
      boolean enabled=true;
      UpdateTrigger() {
        timer.setRepeats(false);
      }
      public void insertUpdate(DocumentEvent e) {
        if(enabled) timer.restart();
      }
      public void removeUpdate(DocumentEvent e) {
        if(enabled) timer.restart();
      }
      public void changedUpdate(DocumentEvent e) {
        if(enabled) timer.restart();
      }
      public void actionPerformed(ActionEvent e) {
        enabled=false;
        try { jColorActionPerformed(null); }
        finally { enabled=true; }
      }
    }
    d.addDocumentListener(new UpdateTrigger());
    

    然后您可能会发现您的代码有更多问题超出了您的问题范围。顺便说一句,仅突出显示关键字不足以正确突出显示语法。您需要一个能够理解句法结构的解析器,尤其是 cmets 和 String 字面量,即使是最小突出显示也是如此。

    您应该研究Chapter 3 of the The Java® Language Specification 以获得完整的图片。

    【讨论】:

    • DocumentFilter 被指定用于 Document 内部的更改,然后是无用的 Swing Timer,顺便说一句,150 的周期对于 JTextComponent 来说非常短,应该 > 500 或使用 invokeLater 代替
    • @mKorbel:您可以使用DocumentFilter,而我更愿意使用适当的StyledDocument(或完整的EditorKit)。但我的回答侧重于提问者实施它的方式。顺便提一句。如果您认为 150 毫秒太短,那么必须 立即 进行解析的 DocumentFilter 应该如何发挥作用?顺便提一句。如果 EDT 忙,Swing 的 Timer 将在稍后自动调用 ActionListener。对我来说,超过 250 毫秒的延迟(当 EDT 不忙时)对于交互式应用程序来说是不可接受的。
    • Swing Timer 不保证事件将在 EDT 结束时执行,这是 invokeLater 的工作,否则无法订购一个事件,其中提到 DocumentFilter 作为监听Document 内部的更改意图更改/修改/追加,我看到很多次(我认为这是错误的,因为 DocumentListener 被指定将事件从/从 Document 分发到 GUI 而不是自己)使用 DocumentListener 并调用事件返回, 拥有 Document 并被 Swing Timer 或 invokeLater 延迟,
    • :-) 特别是 StanislavL 喜欢 DocumentListener 中的 invokeLater,在他的 JTextComponents 解决方法中
    • @mKorbel:Swing Timer 保证在 EDT 上调用 ActionListener,这只有在 EDT 不做其他事情时才有可能。此外,它保证在超过invokeLater 提供的指定时间之前不会调用ActionListener。不需要订购保证,事实上,我的答案代码会故意错过更新,因为它使用Timer.restart() 取消并重新安排待处理的更新。相反,不可能取消待处理的invokeLater 活动。但我同意,无论如何修改事件的来源都是不好的设计。
    猜你喜欢
    • 2019-12-21
    • 1970-01-01
    • 2013-10-28
    • 2015-09-06
    • 2013-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多