假设你有以下字符串:
String s = "The cold hand reaches for the %1$s %2$s Ellesse's";
String old = "old";
String tan = "tan";
String formatted = String.format(s,old,tan); //"The cold hand reaches for the old tan Ellesse's"
假设您想以这个字符串结束,但也要为替换为 String.format
的任何单词设置特定的Span
。
例如,我们还想执行以下操作:
Spannable spannable = new SpannableString(formatted);
spannable.setSpan(new StrikethroughSpan(), oldStart, oldStart+old.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan(new ForegroundColorSpan(Color.BLUE), tanStart, tanStart+tan.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
有没有一种强大的方法来了解old
和tan
的起始指数?
返回"cold"中的"旧",因此这不起作用。
我想,有效的方法是事先搜索%[0-9]$s
,并计算偏移量以解释String.format
中的替换。这似乎是一个令人头疼的问题,我怀疑可能有一种方法像String.format
可以更提供有关其格式细节的信息。嗯,有吗?
我创建了一个适用于spannables的String.format
版本。下载它并像普通版本一样使用它。在您的情况下,您可以将跨度放在格式说明符周围(可能使用字符串.xml)。在输出中,它们将围绕这些说明符被替换的任何内容。
像这样使用Spannables是一个令人头疼的问题 - 这可能是最直接的方法:
String s = "The cold hand reaches for the %1$s %2$s Ellesse's";
String old = "<font color="blue">old</font>";
String tan = "<strike>tan</strike>";
String formatted = String.format(s,old,tan); //The cold hand reaches for the <font color="blue">old</font> <strike>tan</strike> Ellesse's
Spannable spannable = Html.fromHtml(formatted);
问题:这不会放入StrikethroughSpan
。为了StrikethroughSpan
,我们从这个问题中借用了一个自定义TagHandler
。
Spannable spannable = Html.fromHtml(text,null,new MyHtmlTagHandler());
MyTagHandler:
public class MyHtmlTagHandler implements Html.TagHandler {
public void handleTag(boolean opening, String tag, Editable output,
XMLReader xmlReader) {
if (tag.equalsIgnoreCase("strike") || tag.equals("s")) {
processStrike(opening, output);
}
}
private void processStrike(boolean opening, Editable output) {
int len = output.length();
if (opening) {
output.setSpan(new StrikethroughSpan(), len, len, Spannable.SPAN_MARK_MARK);
} else {
Object obj = getLast(output, StrikethroughSpan.class);
int where = output.getSpanStart(obj);
output.removeSpan(obj);
if (where != len) {
output.setSpan(new StrikethroughSpan(), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
private Object getLast(Editable text, Class kind) {
Object[] objs = text.getSpans(0, text.length(), kind);
if (objs.length == 0) {
return null;
} else {
for (int i = objs.length; i > 0; i--) {
if (text.getSpanFlags(objs[i - 1]) == Spannable.SPAN_MARK_MARK) {
return objs[i - 1];
}
}
return null;
}
}
}
我做了一个简单的 Kotlin 扩展函数,应该可以解决String.format
跨越的问题:
fun Context.getStringSpanned(@StringRes resId: Int, vararg formatArgs: Any): Spanned {
var lastArgIndex = 0
val spannableStringBuilder = SpannableStringBuilder(getString(resId, *formatArgs))
for (arg in formatArgs) {
val argString = arg.toString()
lastArgIndex = spannableStringBuilder.indexOf(argString, lastArgIndex)
if (lastArgIndex != -1) {
(arg as? CharSequence)?.let {
spannableStringBuilder.replace(lastArgIndex, lastArgIndex + argString.length, it)
}
lastArgIndex += argString.length
}
}
return spannableStringBuilder
}
一组 Spannable 替换字符串中的%s
占位符,并且没有找到任何足够令人满意的东西,所以我实现了我自己的格式化程序作为 kotlin 字符串扩展。希望它对任何人都有帮助。
fun String.formatSpannable(vararg spans: CharSequence?): Spannable {
val result = SpannableStringBuilder()
when {
spans.size != this.split("%s").size - 1 ->
Log.e("formatSpannable",
"cannot format '$this' with ${spans.size} arguments")
!this.contains("%s") -> result.append(this)
else -> {
var str = this
var spanIndex = 0
while (str.contains("%s")) {
val preStr = str.substring(0, str.indexOf("%s"))
result.append(preStr)
result.append(spans[spanIndex] ?: "")
str = str.substring(str.indexOf("%s") + 2)
spanIndex++
}
if (str.isNotEmpty()) {
result.append(str)
}
}
}
return result
}
然后用法如下
"hello %s kotlin %s world".formatSpannable(span0, span1)
我弹出了同样的问题,但在这里找到了一个非常好的解决方案:Android:如何将Spannable.setSpan与String.format结合起来?
查看@george钢提供的解决方案。他创建了一个自定义版本的 String.format,它保留了跨度。
使用示例:
Spanned toDisplay = SpanFormatter.format(getText(R.string.foo), bar, baz, quux);
使用TextUtils.expandTemplate是最好的解决方案。无需使用第三方库、解析 HTML 或维护自己的代码。
例:
val formattedText = SpannableString.valueOf("world")
formattedText.setSpan(
StyleSpan(Typeface.BOLD), 0, formattedText.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
TextUtils.expandTemplate(
"Hello ^1",
formattedText
)
将创建字符串"你好世界"