使用Span在Android EditText上突出显示语法?

时间:2017-03-14 12:49:04

标签: java android syntax-highlighting spannablestring

我在EditText中为高亮语法创建了简单代码。 首先,我创建了一个HashMap来存储关键字和颜色。

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

然后我为TextWatcher添加了EditText。 在afterTextChanged方法中,我使用以下代码为每个关键字设置颜色

                        ........
                        @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);
                                }

                            }
                        }

这个代码正常工作,如果我输入不同的单词,比如“public void String”,但是当我输入时它不起作用 同一个词,“公众大众”。它只将颜色设置为第一个单词。

enter image description here

我怎样才能做到这一点?谢谢。

3 个答案:

答案 0 :(得分:4)

使用string.indexOf(s)将首次出现。您可以使用正则表达式,而不是使用关键字地图并使用indexOf。我作为一个例子快速写了这个:

以下示例EditText的屏幕截图:

enter image description here

示例:

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;
    }
  }

});

答案 1 :(得分:3)

由于在每个for循环后查找索引的起始位置始终相同,所以我添加一个startIndex来记录更改的起始位置,这里是示例代码:

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();
        }

    }
}

答案 2 :(得分:0)

由于已经接受了一些答案,所以我想给出一个我遇到的问题的答案,那就是当我按退格键并且单词从“ String”更改为“ Strin”或“ Srn”(midle中的退格键)时颜色变为非关键字时,其颜色应保持在应为黑色的位置。为此,我在接受的答案片段之后使用了以下代码。希望对您有所帮助。谢谢!

@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);
                    }

                }

                }