假设您有以下字符串:
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
的起始索引?
请注意,只搜索'old'会在'cold'中返回'old',因此无效。
将工作的内容,我想是在事先搜索%[0-9]$s
,并计算偏移量以考虑String.format
中的替换项。这看起来很令人头疼,我怀疑可能有一种像String.format
这样的方法,它可以提供有关格式化细节的更多信息。嗯,有吗?
答案 0 :(得分:15)
我创建的a version of String.format
适用于spannables。下载并使用它就像普通版本一样。在您的情况下,您将跨越格式说明符(可能使用strings.xml)。在输出中,它们将围绕这些说明符被替换的任何内容。
答案 1 :(得分:12)
使用像这样的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
,我们会从this question借用自定义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;
}
}
}
答案 2 :(得分:3)
我制作了简单的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
}
答案 3 :(得分:0)
我弹出了同样的问题,但是在这里找到了一个非常好的解决方案: Android: How to combine Spannable.setSpan with String.format?
查看@ george-steel提供的解决方案。他创建了custom version of String.format来保留跨度。
示例用法:
Spanned toDisplay = SpanFormatter.format(getText(R.string.foo), bar, baz, quux);
答案 4 :(得分:0)
我需要用一组Spannables替换String中的%s
占位符,但找不到满足要求的东西,因此我将自己的格式化程序实现为kotlin String扩展。希望它能对任何人有帮助。
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)