Android 6.0+上可编辑问题的多个ForegroundColorSpan

时间:2016-01-06 11:18:14

标签: android android-edittext syntax-highlighting android-6.0-marshmallow

如果我在Editable上设置多个跨度,则使用最后一个ForegroundColorSpan。在我看来,这是预期的行为。在Android 6.0+上,最新的设定范围不会优先。

下面是一个完整的工作示例,它突出显示EditText中的数字和字符串:

public class MainActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Create the EditText view
    EditText editText = new EditText(this);
    editText.setLayoutParams(new ViewGroup.LayoutParams(
        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
    editText.setBackground(null);
    editText.setGravity(Gravity.TOP);

    // Patterns to find numbers and strings
    final Pattern[] patterns = {
        Pattern.compile("\\b(\\d*[.]?\\d+)\\b"), // numbers
        Pattern.compile("\".*?\"|'.*?'"), // strings
    };

    // Colors for numbers and strings
    final int[] colors = {
        Color.RED,
        Color.BLUE
    };

    // Add a TextWatcher to highlight numbers and strings
    editText.addTextChangedListener(new TextWatcher() {

      @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 editable) {

        // remove all spans before highlighting new text
        ForegroundColorSpan[] spans = editable.getSpans(0, editable.length(), ForegroundColorSpan.class);
        for (ForegroundColorSpan span : spans) {
          editable.removeSpan(span);
        }

        // first, highlight numbers, next strings
        for (int i = 0; i < patterns.length; i++) {
          Pattern p = patterns[i];
          Matcher m = p.matcher(editable);
          while (m.find()) {
            editable.setSpan(new ForegroundColorSpan(colors[i]), m.start(), m.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

            // print match
            System.out.println(editable.subSequence(m.start(), m.end()));
          }
        }

        // PROBLEM:
        // On Android 6.0+ numbers are highlighted within the string.
        // This is not expected behavior. It worked prior to Android 6.0
      }
    });

    // set some text for testing purposes
    editText.setText("foo \"bar\" baz \"quz 16\"");

    setContentView(editText);
  }

}

如果我启动此Activity并输入文字

  

foo“bar”baz“qux 16”

我希望“bar”和“qux 16”都是蓝色的。但是,在Android 6.0及16上为红色。

以下是截图:

Android 5.1.1(预期行为)

Android 5.1.1

Android 6.0.1

Android 6.0.1

请注意第二个屏幕截图中如何突出显示16

问题:

为什么第一个颜色跨度会覆盖Android 6.0上的最后一个颜色跨度?如何解决此问题?

1 个答案:

答案 0 :(得分:1)

如果在设置新ForegroundColorSpan之前删除所有跨度,那么我会得到所需的行为。不幸的是,如果EditText有大量文字,这还有很多工作要做。

我为修复做了什么:

@Override public void afterTextChanged(Editable editable) {
  if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
    // remove old spans before highlighting
    // this does not work on Android 6.0+
    ForegroundColorSpan[] spans = editable.getSpans(0, editable.length(), ForegroundColorSpan.class);
    for (ForegroundColorSpan span : spans) {
      editable.removeSpan(span);
    }
  }

  for (int i = 0; i < patterns.length; i++) {
    Pattern p = patterns[i];
    Matcher m = p.matcher(editable);
    while (m.find()) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        // Remove any spans in the given range.
        ForegroundColorSpan[] oldspans = editable.getSpans(m.start(), m.end(), ForegroundColorSpan.class);
        for (ForegroundColorSpan span : oldspans) {
          editable.removeSpan(span);
        }
      }
      editable.setSpan(new ForegroundColorSpan(colors[i]), m.start(), m.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
  }
}

我不确定Android 6.0+有什么变化,如果有人有任何见解,我们将不胜感激。