如何解决适用于多种模式的 Android SpannableStringBuilder
我为不同的实体(产品、房间、帖子和用户)设置了不同的屏幕。 所有这些都有不同的 ID,我用它来打开特定的屏幕,以尝试从通过 API 传递到相关屏幕/活动的 ID 参数加载数据。
我特别为产品、房间和帖子考虑的机制是在通知屏幕等上,我想发送编码或标记的字符串;例如下面的产品:
You have bid on <p244 24LED Lampshade />
<p([0-9]*)\s(.*?)\/>(\s|$)
这是一个示例字符串:
String dummyText = "Hi @dan we product test <p1337 product />" +
"\n how about <r123 Room name/> is a nice room" +
"\n what <t234 this post details more about it/> but you should " +
"\n #poprocks woah one #sad";
单次使用产品/帖子/房间似乎可以正常工作,但它会因编码/标记字符串的更多实例而混乱。
但就像我说的,有多个实例,如以下测试:
String dummyText = "Hi @dan we product test <p1337 product /> and were more happy with <p53 car/> " +
"\n eh <r123 Room name/> is a nice room and <r233 dans house/> sucked while <r123 fun time/> was ok " +
// "\n what <t234 this post details more about it/> but you should " +
"\n <t234 view this as well/>" +
"\n #poprocks woah one #sad";
它搞砸了:
这是我的整个代码过程:
String dummyText = "Hi @dan we product test <p1337 product /> and were more happy with <p53 car/> " +
"\n eh <r123 Room name/> is a nice room and <r233 dans house/> sucked while <r123 fun time/> was ok " +
// "\n what <t234 this post details more about it/> but you should " +
"\n <t234 view this as well/>" +
"\n #poprocks woah one #sad";
List<Pattern> patternsList = new ArrayList<>();
patternsList.add(Pattern.compile(REGEX_NOTI_ROOMS)); //0
patternsList.add(Pattern.compile(REGEX_NOTI_PRODUCTS)); //1
patternsList.add(Pattern.compile(REGEX_NOTI_TWEETS)); //2
patternsList.add(Pattern.compile(REGEX_NOTI_MENTION)); //3
patternsList.add(Pattern.compile(REGEX_araBIC_N_NUM_HASHTAG)); //4
holder.row_noti_messageTV.setText(makeSpannable(dummyText,patternsList));
holder.row_noti_messageTV.setMovementMethod(new PkMovementMethod());
相关正则表达式在哪里:
public static final String REGEX_NOTI_ROOMS ="<r([0-9]*)\\s(.*?)\\/>(\\s|$)";
public static final String REGEX_NOTI_PRODUCTS ="<p([0-9]*)\\s(.*?)\\/>(\\s|$)";
public static final String REGEX_NOTI_TWEETS ="<t([0-9]*)\\s(.*?)\\/>(\\s|$)";
public static final String REGEX_araBIC_N_NUM_HASHTAG ="#(\\w*[0-9a-zA-Zء-ي٠-٩]+\\w*[0-9a-zA-Zء-ي٠-٩])";
public static final String REGEX_NOTI_MENTION ="(?:^|\\s|$|[.])@[\\p{L}0-9_]*";
我的 makeSpannable 方法是:
public SpannableStringBuilder makeSpannable(String rawText,List<Pattern> listofPatterns) {
// StringBuffer sb = new StringBuffer();
SpannableStringBuilder spannable = new SpannableStringBuilder(rawText);
for(int i=0; i<listofPatterns.size(); i++)
{
Matcher matcher = null;
if(i==0) //init only
matcher = listofPatterns.get(i).matcher(rawText);
else
matcher = listofPatterns.get(i).matcher(spannable);
while (matcher.find()) {
showLogMessage("jsonParse","hit on iteration" + i + " group == " + matcher.group());
if(i==3 || i == 4)
{
try {
String abbr = matcher.group();
showLogMessage("jsonParse","loop[3 | 4 are normal] == " + i + " group(0) == " + abbr);
int start = matcher.start();
int end = matcher.end();
NotificationSpan ourSpan = new NotificationSpan(Color.BLUE,Color.RED,Color.TRANSPARENT,new IdTitleStore(abbr,abbr,abbr),NotificationAdapter.this);
spannable.setSpan(ourSpan,start,end,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (Exception e) {
e.printstacktrace();
}
}
else if(i<=2) {
try {
String orgGroup = matcher.group();
// get the match
String abbr = matcher.group(2);
showLogMessage("jsonParse","loop == " + i + " group2 == " + abbr);
int startPoint = matcher.start();
int endPoint = matcher.end();
NotificationSpan ourSpan = new NotificationSpan(Color.RED,Color.BLUE,new IdTitleStore(matcher.group(1),orgGroup),NotificationAdapter.this);
Spannable spanText = new SpannableString(abbr);
spanText.setSpan(
ourSpan,abbr.length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
spannable.replace(startPoint,endPoint,spanText);
} catch (Exception e) {
e.printstacktrace();
}
}
}
}
return spannable;
}
注意:NotificationSpan 是我用来存储组 (1) 的自定义跨度,因为它具有 ID,并且从组 (0) 中我可以通过点击推断它是否可能是要打开的帖子、产品或房间实体
任何反馈都应受到赞赏,即使它可以为我指明正确的方向。就像我的方法本身是根本错误的一样,请告诉我。
编辑:有人在评论中指出要从正则表达式中删除多余的 (\s|$),但它似乎什么也没做
我测试的 dummyString 的日志:
String dummyText = "Hi @dan we product test <p1337 product /> and were more happy with <p53 car/> " +
"\n eh <r123 Room name/> is a nice room and <r233 dans house/> sucked while <r123 fun time/> was ok " +
"\n what <t233 this post details more about it/> but you should " +
"\n <t234 view this as well/>" +
"\n #poprocks woah one #sad";
E/jsonParse: hit on iteration0 group(0) == <r123 Room name/>
E/jsonParse: hit on iteration0 group(0) == <r233 dans house/>
E/jsonParse: hit on iteration0 group(0) == <r123 fun time/>
E/jsonParse: hit on iteration1 group(0) == <p1337 product />
E/jsonParse: hit on iteration1 group(0) == <p53 car/>
E/jsonParse: hit on iteration2 group(0) == <t234 view this as well/>
E/jsonParse: hit on iteration3 group(0) == @dan
E/jsonParse: hit on iteration4 group(0) == #poprocks
E/jsonParse: hit on iteration4 group(0) == #sad
匹配器根本没有检测到第一个实例也很奇怪。 即
"\n what <t234 this post details more about it/> but you should "
在循环内匹配多个模式的逻辑本身可能完全有问题,但我似乎真的无法弄清楚。欣赏评论!
EDIT EDIT*:我想我终于明白问题是什么了。在 replace 方法之后添加点它给了我两个建议,notify() 和 notifyAll(),这让我想知道这确实是基于多线程的操作(对其他人来说很明显,但是是的!)。所以多重循环实际上是问题所在。
由于替换调用会更新跨度本身,因此内部循环(而 mathcer.find())在匹配器上没有最新的跨度,它具有不同的/上一个跨度,如果只有发现了一个实例,但由于多个实例的开始和结束相距甚远,因此发生了一些意想不到的事情。在更新后的代码之后,我确实保留了 (\s|$) 以防万一产品/帖子/房间是字符串的结尾,因此它确实匹配而不是保持空白。
public SpannableStringBuilder makeSpannable(String rawText,List<Pattern> listofPatterns) {
SpannableStringBuilder spannable = new SpannableStringBuilder(rawText);
Matcher matcher = null;
for(int i=0; i<listofPatterns.size(); i++)
{
matcher = listofPatterns.get(i).matcher(spannable.toString());
while (matcher.find()) {
showLogMessage("jsonParse","hit on iteration" + i + " group == " + matcher.group());
if(i<=2) {
try {
String orgGroup = matcher.group();
// get the match
String abbr = matcher.group(2);
showLogMessage("jsonParse","span txt of iteration " + i + " going to be group2 == " + abbr);
int startPoint = matcher.start();
int endPoint = matcher.end();
NotificationSpan ourSpan = new NotificationSpan(Color.RED,endPoint-1,spanText);
matcher = listofPatterns.get(i).matcher(spannable);
} catch (Exception e) {
e.printstacktrace();
}
}
else {
try {
String abbr = matcher.group();
showLogMessage("jsonParse","span txt of iteration " + i + " going to be group(0) == " + abbr);
int start = matcher.start();
int end = matcher.end();
NotificationSpan ourSpan = new NotificationSpan(Color.BLUE,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (Exception e) {
e.printstacktrace();
}
}
}
}
return spannable;
}
附加说明:我认为最好解释一下为什么我将循环分成两部分来开始。原因很简单,我的 REGEX 的前 3 个我 100% 肯定有 group(2) 元素,我已经实现了点击 ID 机制。最后两个是普通的主题标签,并提及没有 group(2) 的标签。
这是最终的工作外观,希望我不会遇到任何性能问题:
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。