是否有可能为一个TextView内的不同文本块设置多个样式?
例如,我将文本设置如下:
tv.setText(line1 + "\n" + line2 + "\n" + word1 + "\t" + word2 + "\t" + word3);
是否可以为每个文本元素设置不同的样式?例如,行1加粗,字1斜体等。
开发者指南的常见任务和如何在Android中执行这些任务包括选择,突出显示或样式化文本部分:
// Get our EditText object.
EditText vw = (EditText)findViewById(R.id.text);
// Set the EditText's text.
vw.setText("Italic, highlighted, bold.");
// If this were just a TextView, we could do:
// vw.setText("Italic, highlighted, bold.", TextView.BufferType.SPANNABLE);
// to force it to use Spannable storage so styles can be attached.
// Or we could specify that in the XML.
// Get the EditText's internal text storage
Spannable str = vw.getText();
// Create our span sections, and assign a format to each.
str.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
str.setSpan(new BackgroundColorSpan(0xFFFFFF00), 8, 19, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
str.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 21, str.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
但这使用了文本中的显式位置编号。有更干净的方法吗?
使用SpannableString是实现这一目标的好方法
我使用了几个函数使其易于应用,我将首先解释每个函数的思想,然后显示代码:
String.getAllIndexOf(pattern: String): This will search the patter on the string and return an index list of where the pattern start. Ex: given the string "abcdefabc" and I call the method passing the "abc" as the searched pattern, the method should return the list: listOf(0, 6)
The is a class to receive the pattern and a list of styles (in case you desire to apply different styles to the same pattern in sequence)
SpannableString.applyStyle(context: Context, vararg patternAndStyles: PatternAndStyles): This will apply the styles to the given patterns
现在,在代码上:
getAllIndexOf
fun String.getAllIndexOf(pattern: String): List<Int> {
val allRecordsOfText = mutableListOf<Int>()
var index = 0
while(index >= 0) {
val newStart = if (allRecordsOfText.isEmpty()) {
0
} else {
allRecordsOfText.last() + pattern.length
}
index = this.subSequence(newStart, this.length).indexOf(pattern)
if (index >= 0) {
allRecordsOfText.add(newStart + index)
}
}
return allRecordsOfText.toList()
}
Class to receive the pattern and styles
@Parcelize
class PatternAndStyles(
val pattern: String,
val styles: List<Int>
) : Parcelable
applyStyle
fun SpannableString.applyStyle(context: Context, vararg patternAndStyles: PatternAndStyles) {
for (patternStyle in patternAndStyles.toList()) {
this.toString().getAllIndexOf(patternStyle.pattern).forEachIndexed { index, start ->
val end = start + patternStyle.pattern.length
val styleIndex = if (patternStyle.styles.size > index) index else patternStyle.styles.size - 1
this.setSpan(
TextAppearanceSpan(context, patternStyle.styles[styleIndex]),
start,
end,
SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
}
How to use it in the end
val stringToApplyStyle = "abc def abc def"
val text = SpannableString(stringToApplyStyle)
text.applyStyle(
this.applicationContext,
PatternAndStyles("abc", listOf(R.style.Style1, R.style.Style2)),
PatternAndStyles("def", listOf(R.style.Style3))
)
The output:
当它被传递给样式到模式“abc”时,两者都被使用
但是在模式“def”中,第二条记录重用了列表中给出的最后一个样式
我也遇到了同样的问题。我可以使用fromHtml,但我现在是android,不是web,所以我决定尝试一下。我必须本地化这个所以我用字符串替换的概念给了它一个机会。我在TextView上设置的风格是主要的风格,然后只是格式的其他作品。
我希望这能帮助其他人做同样的事情——我不知道为什么在这个框架中这不是更容易。
我的字符串是这样的:
<string name="my_text">{0} You will need a {1} to complete this assembly</string>
<string name="text_sub0">1:</string>
<string name="text_sub1">screwdriver, hammer, and measuring tape</string>
下面是一些款式:
<style name="MainStyle">
<item name="android:textSize">@dimen/regular_text</item>
<item name="android:textColor">@color/regular_text</item>
</style>
<style name="style0">
<item name="android:textSize">@dimen/paragraph_bullet</item>
<item name="android:textColor">@color/standout_text</item>
<item name="android:textStyle">bold</item>
</style>
<style name="style1">
<item name="android:textColor">@color/standout_light_text</item>
<item name="android:textStyle">italic</item>
</style>
下面是调用formatStyles方法的代码:
SpannableString formattedSpan = formatStyles(getString(R.string.my_text), getString(R.string.text_sub0), R.style.style0, getString(R.string.main_text_sub1), R.style.style1);
textView.setText(formattedSpan, TextView.BufferType.SPANNABLE);
格式方法:
private SpannableString formatStyles(String value, String sub0, int style0, String sub1, int style1)
{
String tag0 = "{0}";
int startLocation0 = value.indexOf(tag0);
value = value.replace(tag0, sub0);
String tag1 = "{1}";
int startLocation1 = value.indexOf(tag1);
if (sub1 != null && !sub1.equals(""))
{
value = value.replace(tag1, sub1);
}
SpannableString styledText = new SpannableString(value);
styledText.setSpan(new TextAppearanceSpan(getActivity(), style0), startLocation0, startLocation0 + sub0.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if (sub1 != null && !sub1.equals(""))
{
styledText.setSpan(new TextAppearanceSpan(getActivity(), style1), startLocation1, startLocation1 + sub1.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return styledText;
}