¿Cómo cambiar el color de palabras específicas en un JTextPane?

¿Cómo cambio el color de palabras específicas en un JTextPane solo mientras el usuario está escribiendo? ¿Debo anular el método JTextPane paintComponent ?

Sobrescribir paintComponent no lo ayudará.

Esto no es fácil, pero tampoco imposible. Algo como esto te ayudará a:

 DefaultStyledDocument document = new DefaultStyledDocument(); JTextPane textpane = new JTextPane(document); StyleContext context = new StyleContext(); // build a style Style style = context.addStyle("test", null); // set some style properties StyleConstants.setForeground(style, Color.BLUE); // add some data to the document document.insertString(0, "", style); 

Es posible que deba modificar esto, pero al menos le muestra por dónde empezar.

No. No debes anular el método paintComponent (). En su lugar, debe usar StyledDocument . También debe delimitar las palabras por su cuenta.

Aquí está la demostración, que convierte “público”, “protegido” y “privado” en rojo cuando se escribe, como un simple editor de código:

enter image description here

 import javax.swing.*; import java.awt.*; import javax.swing.text.*; public class Test extends JFrame { private int findLastNonWordChar (String text, int index) { while (--index >= 0) { if (String.valueOf(text.charAt(index)).matches("\\W")) { break; } } return index; } private int findFirstNonWordChar (String text, int index) { while (index < text.length()) { if (String.valueOf(text.charAt(index)).matches("\\W")) { break; } index++; } return index; } public Test () { setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(400, 400); setLocationRelativeTo(null); final StyleContext cont = StyleContext.getDefaultStyleContext(); final AttributeSet attr = cont.addAttribute(cont.getEmptySet(), StyleConstants.Foreground, Color.RED); final AttributeSet attrBlack = cont.addAttribute(cont.getEmptySet(), StyleConstants.Foreground, Color.BLACK); DefaultStyledDocument doc = new DefaultStyledDocument() { public void insertString (int offset, String str, AttributeSet a) throws BadLocationException { super.insertString(offset, str, a); String text = getText(0, getLength()); int before = findLastNonWordChar(text, offset); if (before < 0) before = 0; int after = findFirstNonWordChar(text, offset + str.length()); int wordL = before; int wordR = before; while (wordR <= after) { if (wordR == after || String.valueOf(text.charAt(wordR)).matches("\\W")) { if (text.substring(wordL, wordR).matches("(\\W)*(private|public|protected)")) setCharacterAttributes(wordL, wordR - wordL, attr, false); else setCharacterAttributes(wordL, wordR - wordL, attrBlack, false); wordL = wordR; } wordR++; } } public void remove (int offs, int len) throws BadLocationException { super.remove(offs, len); String text = getText(0, getLength()); int before = findLastNonWordChar(text, offs); if (before < 0) before = 0; int after = findFirstNonWordChar(text, offs); if (text.substring(before, after).matches("(\\W)*(private|public|protected)")) { setCharacterAttributes(before, after - before, attr, false); } else { setCharacterAttributes(before, after - before, attrBlack, false); } } }; JTextPane txt = new JTextPane(doc); txt.setText("public class Hi {}"); add(new JScrollPane(txt)); setVisible(true); } public static void main (String args[]) { new Test(); } } 

El código no es tan hermoso ya que lo escribí rápidamente, pero funciona. Y espero que te dé una pista.

Otra solución es usar un DocumentFilter .

Aquí hay un ejemplo:

Crea una clase que amplíe DocumentFilter:

 private final class CustomDocumentFilter extends DocumentFilter { private final StyledDocument styledDocument = yourTextPane.getStyledDocument(); private final StyleContext styleContext = StyleContext.getDefaultStyleContext(); private final AttributeSet greenAttributeSet = styleContext.addAttribute(styleContext.getEmptySet(), StyleConstants.Foreground, Color.GREEN); private final AttributeSet blackAttributeSet = styleContext.addAttribute(styleContext.getEmptySet(), StyleConstants.Foreground, Color.BLACK); // Use a regular expression to find the words you are looking for Pattern pattern = buildPattern(); @Override public void insertString(FilterBypass fb, int offset, String text, AttributeSet attributeSet) throws BadLocationException { super.insertString(fb, offset, text, attributeSet); handleTextChanged(); } @Override public void remove(FilterBypass fb, int offset, int length) throws BadLocationException { super.remove(fb, offset, length); handleTextChanged(); } @Override public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attributeSet) throws BadLocationException { super.replace(fb, offset, length, text, attributeSet); handleTextChanged(); } /** * Runs your updates later, not during the event notification. */ private void handleTextChanged() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { updateTextStyles(); } }); } /** * Build the regular expression that looks for the whole word of each word that you wish to find. The "\\b" is the beginning or end of a word boundary. The "|" is a regex "or" operator. * @return */ private Pattern buildPattern() { StringBuilder sb = new StringBuilder(); for (String token : ALL_WORDS_THAT_YOU_WANT_TO_FIND) { sb.append("\\b"); // Start of word boundary sb.append(token); sb.append("\\b|"); // End of word boundary and an or for the next word } if (sb.length() > 0) { sb.deleteCharAt(sb.length() - 1); // Remove the trailing "|" } Pattern p = Pattern.compile(sb.toString()); return p; } private void updateTextStyles() { // Clear existing styles styledDocument.setCharacterAttributes(0, yourTextPane.getText().length(), blackAttributeSet, true); // Look for tokens and highlight them Matcher matcher = pattern.matcher(yourTextPane.getText()); while (matcher.find()) { // Change the color of recognized tokens styledDocument.setCharacterAttributes(matcher.start(), matcher.end() - matcher.start(), greenAttributeSet, false); } } } 

Todo lo que necesita hacer entonces es aplicar el DocumentFilter que creó a su JTextPane siguiente manera:

 ((AbstractDocument) yourTextPane.getDocument()).setDocumentFilter(new CustomDocumentFilter()); 

Puede extender DefaultStyledDocument como lo hice aquí para un editor de SQL que estoy construyendo con coloreado de texto de palabra clave …

  import java.util.ArrayList; import java.util.List; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Style; public class KeywordStyledDocument extends DefaultStyledDocument { private static final long serialVersionUID = 1L; private Style _defaultStyle; private Style _cwStyle; public KeywordStyledDocument(Style defaultStyle, Style cwStyle) { _defaultStyle = defaultStyle; _cwStyle = cwStyle; } public void insertString (int offset, String str, AttributeSet a) throws BadLocationException { super.insertString(offset, str, a); refreshDocument(); } public void remove (int offs, int len) throws BadLocationException { super.remove(offs, len); refreshDocument(); } private synchronized void refreshDocument() throws BadLocationException { String text = getText(0, getLength()); final List list = processWords(text); setCharacterAttributes(0, text.length(), _defaultStyle, true); for(HiliteWord word : list) { int p0 = word._position; setCharacterAttributes(p0, word._word.length(), _cwStyle, true); } } private static List processWords(String content) { content += " "; List hiliteWords = new ArrayList(); int lastWhitespacePosition = 0; String word = ""; char[] data = content.toCharArray(); for(int index=0; index < data.length; index++) { char ch = data[index]; if(!(Character.isLetter(ch) || Character.isDigit(ch) || ch == '_')) { lastWhitespacePosition = index; if(word.length() > 0) { if(isReservedWord(word)) { hiliteWords.add(new HiliteWord(word,(lastWhitespacePosition - word.length()))); } word=""; } } else { word += ch; } } return hiliteWords; } private static final boolean isReservedWord(String word) { return(word.toUpperCase().trim().equals("CROSS") || word.toUpperCase().trim().equals("CURRENT_DATE") || word.toUpperCase().trim().equals("CURRENT_TIME") || word.toUpperCase().trim().equals("CURRENT_TIMESTAMP") || word.toUpperCase().trim().equals("DISTINCT") || word.toUpperCase().trim().equals("EXCEPT") || word.toUpperCase().trim().equals("EXISTS") || word.toUpperCase().trim().equals("FALSE") || word.toUpperCase().trim().equals("FETCH") || word.toUpperCase().trim().equals("FOR") || word.toUpperCase().trim().equals("FROM") || word.toUpperCase().trim().equals("FULL") || word.toUpperCase().trim().equals("GROUP") || word.toUpperCase().trim().equals("HAVING") || word.toUpperCase().trim().equals("INNER") || word.toUpperCase().trim().equals("INTERSECT") || word.toUpperCase().trim().equals("IS") || word.toUpperCase().trim().equals("JOIN") || word.toUpperCase().trim().equals("LIKE") || word.toUpperCase().trim().equals("LIMIT") || word.toUpperCase().trim().equals("MINUS") || word.toUpperCase().trim().equals("NATURAL") || word.toUpperCase().trim().equals("NOT") || word.toUpperCase().trim().equals("NULL") || word.toUpperCase().trim().equals("OFFSET") || word.toUpperCase().trim().equals("ON") || word.toUpperCase().trim().equals("ORDER") || word.toUpperCase().trim().equals("PRIMARY") || word.toUpperCase().trim().equals("ROWNUM") || word.toUpperCase().trim().equals("SELECT") || word.toUpperCase().trim().equals("SYSDATE") || word.toUpperCase().trim().equals("SYSTIME") || word.toUpperCase().trim().equals("SYSTIMESTAMP") || word.toUpperCase().trim().equals("TODAY") || word.toUpperCase().trim().equals("TRUE") || word.toUpperCase().trim().equals("UNION") || word.toUpperCase().trim().equals("UNIQUE") || word.toUpperCase().trim().equals("WHERE")); } } 

Simplemente agréguelo a su clase de la siguiente manera:

  import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextPane; import javax.swing.text.BadLocationException; import javax.swing.text.Style; import javax.swing.text.StyleConstants; import javax.swing.text.StyleContext; public class SQLEditor extends JFrame { private static final long serialVersionUID = 1L; public SQLEditor() { StyleContext styleContext = new StyleContext(); Style defaultStyle = styleContext.getStyle(StyleContext.DEFAULT_STYLE); Style cwStyle = styleContext.addStyle("ConstantWidth", null); StyleConstants.setForeground(cwStyle, Color.BLUE); StyleConstants.setBold(cwStyle, true); final JTextPane pane = new JTextPane(new KeywordStyledDocument(defaultStyle, cwStyle)); pane.setFont(new Font("Courier New", Font.PLAIN, 12)); JScrollPane scrollPane = new JScrollPane(pane); getContentPane().add(scrollPane, BorderLayout.CENTER); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(375, 400); } public static void main(String[] args) throws BadLocationException { SQLEditor app = new SQLEditor(); app.setVisible(true); } } 

Aquí está la clase de HiliteWord que falta …

 public class HiliteWord { int _position; String _word; public HiliteWord(String word, int position) { _position = position; _word = word; } }