5

I created simple code for highlight syntax in EditText. First i created a HashMap to store keywords and colors.

                        Map<String,Integer> map = new HashMap<>();
                        map.put("public",Color.CYAN);
                        map.put("void", Color.BLUE);
                        map.put("String",Color.RED);

Then I added a TextWatcher for the EditText. In afterTextChanged method I used following code to set colors to each keyword,

                        ........
                        @Override
                        public void afterTextChanged(Editable editable) {
                            String string = editable.toString();
                            String[] split = string.split("\\s");
                            for(int i = 0 ; i < split.length ; i++){
                                String s = split[i];
                                if(map.containsKey(s)){
                                    int index = string.indexOf(s);
                                    int color = map.get(s);
                                    editable.setSpan(new ForegroundColorSpan(color),
                                            index,
                                            index + s.length(),
                                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                                }

                            }
                        }

This code is working, if i type different words, like "public void String", but It's not working when I type same word, "public public public". It only set color to the first word.

enter image description here

How can i make this work ? Thank you.

3 Answers 3

8

Using string.indexOf(s) will get the first occurrence. Instead of having a map of keywords and using indexOf you could use a regular expression. I wrote this up real quick as an example:

Screenshot of example EditText below:

enter image description here

Example:

final EditText editText = new EditText(this);
editText.addTextChangedListener(new TextWatcher() {

  ColorScheme keywords = new ColorScheme(
      Pattern.compile(
          "\\b(package|transient|strictfp|void|char|short|int|long|double|float|const|static|volatile|byte|boolean|class|interface|native|private|protected|public|final|abstract|synchronized|enum|instanceof|assert|if|else|switch|case|default|break|goto|return|for|while|do|continue|new|throw|throws|try|catch|finally|this|super|extends|implements|import|true|false|null)\\b"),
      Color.CYAN
  );

  ColorScheme numbers = new ColorScheme(
      Pattern.compile("(\\b(\\d*[.]?\\d+)\\b)"),
      Color.BLUE
  );

  final ColorScheme[] schemes = { keywords, numbers };

  @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {

  }

  @Override public void onTextChanged(CharSequence s, int start, int before, int count) {

  }

  @Override public void afterTextChanged(Editable s) {
    removeSpans(s, ForegroundColorSpan.class);
    for (ColorScheme scheme : schemes) {
      for(Matcher m = scheme.pattern.matcher(s); m.find();) {
        s.setSpan(new ForegroundColorSpan(scheme.color),
            m.start(),
            m.end(),
            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
      }
    }
  }

  void removeSpans(Editable e, Class<? extends CharacterStyle> type) {
    CharacterStyle[] spans = e.getSpans(0, e.length(), type);
    for (CharacterStyle span : spans) {
      e.removeSpan(span);
    }
  }

  class ColorScheme {
    final Pattern pattern;
    final int color;

     ColorScheme(Pattern pattern, int color) {
      this.pattern = pattern;
      this.color = color;
    }
  }

});
Sign up to request clarification or add additional context in comments.

1 Comment

I used a HashMap because, I can store keyword and color.But your code gives me another solution for my question. Thank you. I'm accepting @moon's answer, because I think it directly answered the question. :-)
6

Since the start position for finding index always the same after each for loop, so I add a startIndex to record the changed start position, here the sample code:

public void afterTextChanged(Editable editable) {
    String string = editable.toString();
    String[] split = string.split("\\s");

    int startIndex = 0;
    for(int i = 0 ; i < split.length ; i++){
        String s = split[i];
        if(map.containsKey(s)){

            int index = string.indexOf(s, startIndex);
            int color = map.get(s);
            editable.setSpan(new ForegroundColorSpan(color),
                    index,
                    index + s.length(),
                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

            startIndex = index + s.length();
        }

    }
}

1 Comment

Thank you. I tried your code and it's working fine. Also i tried @Jared Rummler 's answer. Both answers useful.
0

As some answers are already accepted I would like to give an answer to a problem which I faced and that was when I pressed backspace and the word changes from "String" to "Strin" or "Srn"(backspace in midle) The color remains same where as it should be black as it is becoming non keyword. For that I have used the following code just after the snippet of the accepted answer. I hope it will help someone. Thanks!

@Override
            public void afterTextChanged(Editable s) {
                String string = s.toString();
                String[] split = string.split("\\s");

                int startIndex = 0;
                for(int i = 0 ; i < split.length ; i++){
                    String editable = split[i];
                    if(map.containsKey(editable)){

                        int index = string.indexOf(editable, startIndex);
                        int color = map.get(editable);
                        s.setSpan(new ForegroundColorSpan(color),
                                index,
                                index + editable.length(),
                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

                        startIndex = index + editable.length();
                    }
                    if(!map.containsKey(editable)) {
                        int index = string.indexOf(editable, startIndex);

                        s.setSpan(new ForegroundColorSpan(Color.BLACK),
                                index,
                                index + editable.length(),
                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }

                }

                }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.