Android 6.0+ 上关于可编辑问题的多个 ForegroundColorSpan
Multiple ForegroundColorSpan on Editable issue on Android 6.0+
如果我在 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(预期行为)
Android6.0.1
请注意 16
在第二个屏幕截图中是如何突出显示的。
问题:
为什么第一个颜色跨度覆盖 Android 6.0 上的最后一个颜色跨度,我该如何解决这个问题?
如果我在设置新 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+ 中发生了什么变化,如果有人有任何见解,我们将不胜感激。
如果我在 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(预期行为)
Android6.0.1
请注意 16
在第二个屏幕截图中是如何突出显示的。
问题:
为什么第一个颜色跨度覆盖 Android 6.0 上的最后一个颜色跨度,我该如何解决这个问题?
如果我在设置新 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+ 中发生了什么变化,如果有人有任何见解,我们将不胜感激。