Android: 突出显示 SpannableString 中的 <em> 个标签

Android: Highlight <em> tags in SpannableString

我有以下代码:

public static SpannableString getSpannable ( String content )
{
    SpannableString s = new SpannableString ( content );

    Matcher m = Pattern.compile ( "<em>(.*?)</em>" ).matcher ( content );

    while ( m.find () )
    { s.setSpan ( new BackgroundColorSpan ( R.color.colorItalic ), m.start ( 1 ), m.end ( 1 ), 0 ); }

    return s;
}

它获取所有 <em>(.*?)</em> 并根据您各自的位置将 BackgroundColorSpan 设置为第一组 (.*?)

效果很好!但问题是 <em> 也在字符串内容中...设置所有跨度后,如何从 SpannableString 中删除 <em>

代替SpannableString使用SpannableStringBuilder并使用replace方法。

试试这个代码,

String content = "Yeah, <em>This</em> is bold.";

SpannableStringBuilder s = new SpannableStringBuilder(content);

String startTag = "<em>";
int startTagLength = startTag.length();
String endTag = "</em>";
int endTagLength = endTag.length();
Matcher m = Pattern.compile(startTag + "(.*?)" + endTag).matcher(content);

while (m.find()) {
    Log.e(TAG, "" + m.start(1));
    Log.e(TAG, "" + m.end(1));
    s.setSpan(new BackgroundColorSpan(R.color.red), m.start(1), m.end(1), 0);
    s.replace(m.end(1), m.end(1) + endTagLength, "");
    s.replace(m.start(1) - startTagLength, m.start(1), "");
}

TextView out = (TextView) findViewById(R.id.out);
out.setText(s);

编辑

Caique Monteiro Araujo's 在评论中提到它不适用于多个标签。这样做的原因是字符串长度在第二次迭代中变得混乱,因为我在 while 循环本身中替换了字符串。

我很高兴 Caique Monteiro Araujo 找到了解决方法。以下是我的。我使用 TreeMap 来存储 (start, end) 对并在另一个循环中替换标签。不知道这样算不算矫枉过正。

String content = "<em>This</em> is <em>bold</em>.";

SpannableStringBuilder s = new SpannableStringBuilder(content);

String startTag = "<em>";
int startTagLength = startTag.length();
String endTag = "</em>";
int endTagLength = endTag.length();
Matcher m = Pattern.compile(startTag + "(.*?)" + endTag).matcher(content);

// TreeMap to store the start and end pair
TreeMap<Integer, Integer> pair = new TreeMap<>();
while (m.find()) {

    // Store the start and end
    pair.put(m.start(1), m.end(1));
    s.setSpan(new BackgroundColorSpan(R.color.red), m.start(1), m.end(1), 0);
}

// Use descendingMap to reverse the Map
NavigableMap<Integer, Integer> reversePair = pair.descendingMap();

// Replace the tags starting from the last occurrence to avoid messing the length
for (Integer key : reversePair.keySet()) {
    Integer end = reversePair.get(key);
    s.replace(end, end + endTagLength, "");
    s.replace(key - startTagLength, key, "");
}

TextView out = (TextView) findViewById(R.id.out);
out.setText(s);

我通过@K Neeraj Lal 的帮助找到了解决方案,对此我深表感谢。关键是,他的回答在 while 循环中不起作用。因此,在删除字符后,我必须创建一个计数器来更新字符位置。这是必要的,因为 Matchercontent 变量相关联。因此,当它替换 s 变量时,它会改变你的长度,但内容变量仍然具有相同的长度(Matcher 链接到的地方)。解决方案代码为:

public static SpannableStringBuilder getSpannable ( String content )
{
    SpannableStringBuilder s = new SpannableStringBuilder ( content );

    String startAt = "<em>";
    String endAt   = "</em>";

    Matcher m = Pattern.compile ( startAt + "(.*?)" + endAt ).matcher ( content );

    int counter = 0, length  = startAt.length() + endAt.length();
    int startA, startB, endA, endB;

    while ( m.find () )
    {
        startA = m.start()-counter;
        endA   = m.end(1)-counter;
        startB = m.start(1)-counter;
        endB   = m.end()-counter;

        s.setSpan ( new BackgroundColorSpan ( R.color.colorItalic ), startB, endA, 0 );
        s.replace ( endA, endB, "" );
        s.replace ( startA, startB, "" );

        counter += length;
    }

    return s;
}

无论如何,我不知道这是不是最好的方法。