有可能在一个TextView里面有多个样式吗?

是否有可能为一个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);

但这使用了文本中的显式位置编号。有更干净的方法吗?

296489 次浏览

尝试Html.fromHtml(),并用粗体和斜体HTML标签标记您的文本,例如:

Spanned text = Html.fromHtml("This mixes <b>bold</b> and <i>italic</i> stuff");
textView.setText(text);

如果有人想知道如何做到这一点,这里有一种方法:(再次感谢马克!)

mBox = new TextView(context);
mBox.setText(Html.fromHtml("<b>" + title + "</b>" +  "<br />" +
"<small>" + description + "</small>" + "<br />" +
"<small>" + DateAdded + "</small>"));

有关此方法支持的标记的非正式列表,请参考这个链接或这个问题:哪些HTML标签支持Android TextView?

支持的标签列表如下:

如果使用字符串资源,则可以添加一些简单的样式,例如使用HTML标记的粗体或斜体。目前支持的标签是:B(粗体),I(斜体),U(下划线),TT(单行),BIGSMALLSUP(上标),SUB(下标),STRIKE(划线)。例如,在res/values/strings.xml中,你可以这样声明:

<resource>
<string id="@+id/styled_welcome_message">We are <b><i>so</i></b> glad to see you.</string>
</resources>

(来自http://developer.android.com/guide/faq/commontasks.html#selectingtext - Web档案链接,<resource>错别字是原来的!)

它还表明在简单的情况下并不真正需要Html.fromHtml

有点离题,但我觉得这个太有用了,不能不在这里提一下。

如果我们想从string.xml资源读取Html文本,从而使其易于本地化。CDATA使这成为可能:

<string name="my_text">
<![CDATA[
<b>Autor:</b> Mr Nice Guy<br/>
<b>Contact:</b> myemail@grail.com<br/>
<i>Copyright © 2011-2012 Intergalactic Spacebar Confederation </i>
]]>
</string>

从我们的Java代码中,我们现在可以像这样使用它:

TextView tv = (TextView) findViewById(R.id.myTextView);
tv.setText(Html.fromHtml(getString(R.string.my_text)));

我没想到这有用。但事实确实如此。

希望它对你们中的一些人有用!

如果你不喜欢使用html,你可以创建一个styles.xml并像这样使用它:

TextView tv = (TextView) findViewById(R.id.textview);
SpannableString text = new SpannableString(myString);


text.setSpan(new TextAppearanceSpan(getContext(), R.style.myStyle), 0, 5, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setSpan(new TextAppearanceSpan(getContext(), R.style.myNextStyle), 6, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);


tv.setText(text, TextView.BufferType.SPANNABLE);

我也遇到了同样的问题。我可以使用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;
}

现在<b>元素已弃用。<strong>渲染为<b><em>渲染为<i>

tv.setText(Html.fromHtml("<strong>bold</strong> and <em>italic</em> "));

这对我来说很好

事实上,除了Html对象,你也可以使用Spannable类型类,例如textappearance espan或TypefaceSpan和SpannableString一起。Html类也使用这些机制。但是使用Spannable类型类,您有更多的自由。

如果你想在xml中添加样式文本,你可以创建一个自定义视图扩展TextView并覆盖setText():

public class HTMLStyledTextView extends TextView
{
public HTMLStyledTextView(Context context) {
super(context);
}


public HTMLStyledTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}


public HTMLStyledTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}


@Override
public void setText(CharSequence text, BufferType type)
{
super.setText(Html.fromHtml(text.toString()), type);
}
}

然后,你可以像这样使用它(用你的包名替换PACKAGE_NAME):

<PACKAGE_NAME.HTMLStyledTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="<![CDATA[
<b>Bolded Text:</b> Non-Bolded Text
]]>"
/>

这里有一个简单的方法,使用HTMLBuilder

    myTextView.setText(new HtmlBuilder().
open(HtmlBuilder.Type.BOLD).
append("Some bold text ").
close(HtmlBuilder.Type.BOLD).
open(HtmlBuilder.Type.ITALIC).
append("Some italic text").
close(HtmlBuilder.Type.ITALIC).
build()
);

结果:

# EYZ0 # EYZ1

如上所述,使用TextView.setText(Html.fromHtml(String))

在Html格式的字符串中使用这些标签:

<a href="...">
<b>
<big>
<blockquote>
<br>
<cite>
<dfn>
<div align="...">
<em>
<font size="..." color="..." face="...">
<h1>
<h2>
<h3>
<h4>
<h5>
<h6>
<i>
<img src="...">
<p>
<small>
<strike>
<strong>
<sub>
<sup>
<tt>
<u>

http://commonsware.com/blog/Android/2010/05/26/html-tags-supported-by-textview.html

这可能就像利用String的length()方法一样简单:

  1. 将Strings XML文件中的文本字符串拆分为尽可能多的子字符串(从Android的角度来看是一个单独的字符串),因为你需要不同的风格,所以它可以像:str1, str2, str3(在你的情况下),当连接在一起时,就是你使用的整个单一字符串。

  2. 然后简单地遵循“Span”方法,就像您在代码中展示的那样——但不是单个字符串,而是将所有子字符串合并为单个字符串,每个子字符串具有不同的自定义样式。

你仍然使用这些数字,但不是直接使用——它们现在不再采用硬编码形式(就像在你的代码中那样),但它们被组合length()方法所取代(注意在str.length()之前和后面加上两颗星来代替绝对数字,以消除变化):

str.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, **str.length()**, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

为第一个字符串的大小,然后为第二个字符串的大小Str.length () + 1, Str.length () + str2.length(),等等,所有的子字符串,而不是例如0,7或8,19等等…

使用SpannableString而不是html标记会更轻一些。它帮助我看到可视化的例子,所以这里是一个补充的答案。

enter image description here

这是一个TextView

// set the text
SpannableString s1 = new SpannableString("bold\n");
SpannableString s2 = new SpannableString("italic\n");
SpannableString s3 = new SpannableString("foreground color\n");
SpannableString s4 = new SpannableString("background color\n");
SpannableString s5 = new SpannableString("underline\n");
SpannableString s6 = new SpannableString("strikethrough\n");
SpannableString s7 = new SpannableString("bigger\n");
SpannableString s8 = new SpannableString("smaller\n");
SpannableString s9 = new SpannableString("font\n");
SpannableString s10 = new SpannableString("URL span\n");
SpannableString s11 = new SpannableString("clickable span\n");
SpannableString s12 = new SpannableString("overlapping spans\n");


// set the style
int flag = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
s1.setSpan(new StyleSpan(Typeface.BOLD), 0, s1.length(), flag);
s2.setSpan(new StyleSpan(Typeface.ITALIC), 0, s2.length(), flag);
s3.setSpan(new ForegroundColorSpan(Color.RED), 0, s3.length(), flag);
s4.setSpan(new BackgroundColorSpan(Color.YELLOW), 0, s4.length(), flag);
s5.setSpan(new UnderlineSpan(), 0, s5.length(), flag);
s6.setSpan(new StrikethroughSpan(), 0, s6.length(), flag);
s7.setSpan(new RelativeSizeSpan(2), 0, s7.length(), flag);
s8.setSpan(new RelativeSizeSpan(0.5f), 0, s8.length(), flag);
s9.setSpan(new TypefaceSpan("monospace"), 0, s9.length(), flag);
s10.setSpan(new URLSpan("https://developer.android.com"), 0, s10.length(), flag);
s11.setSpan(new ClickableSpan() {
@Override
public void onClick(View widget) {
Toast.makeText(getApplicationContext(), "Span clicked", Toast.LENGTH_SHORT).show();
}
}, 0, s11.length(), flag);
s12.setSpan(new ForegroundColorSpan(Color.RED), 0, 11, flag);
s12.setSpan(new BackgroundColorSpan(Color.YELLOW), 4, s12.length(), flag);
s12.setSpan(new UnderlineSpan(), 4, 11, flag);


// build the string
SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(s1);
builder.append(s2);
builder.append(s3);
builder.append(s4);
builder.append(s5);
builder.append(s6);
builder.append(s7);
builder.append(s8);
builder.append(s9);
builder.append(s10);
builder.append(s11);
builder.append(s12);


// set the text view with the styled text
textView.setText(builder);
// enables clicking on spans for clickable span and url span
textView.setMovementMethod(LinkMovementMethod.getInstance());

进一步的研究

这个例子最初的灵感来自在这里

使用一个辅助的可扩展类作为Android字符串资源共享在网页底部。您可以通过创建CharSquences并给它们一个样式来实现这一点。

但在他们给我们的例子中,只是粗体,斜体,甚至是彩色文本。我需要在CharSequence中包装几个样式,以便将它们设置在TextView中。所以我给这个类(我命名为CharSequenceStyles)添加了这个函数。

public static CharSequence applyGroup(LinkedList<CharSequence> content){
SpannableStringBuilder text = new SpannableStringBuilder();
for (CharSequence item : content) {
text.append(item);
}
return text;
}

在视图中我添加了这个。

            message.push(postMessageText);
message.push(limitDebtAmount);
message.push(pretMessageText);
TextView.setText(CharSequenceStyles.applyGroup(message));

我希望这对你有帮助!

Spanny使SpannableString更容易使用。

Spanny spanny = new Spanny("Underline text", new UnderlineSpan())
.append("\nRed text", new ForegroundColorSpan(Color.RED))
.append("\nPlain text");
textView.setText(spanny)

作为Jon ,对我来说,这是最好的解决方案,你不需要在运行时设置任何文本,只使用这个自定义类HtmlTextView

public class HtmlTextView extends TextView {


public HtmlTextView(Context context) {
super(context);
}


public HtmlTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}


public HtmlTextView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}


@TargetApi(21)
public HtmlTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}


@Override
public void setText(CharSequence s,BufferType b){
super.setText(Html.fromHtml(s.toString()),b);
}


}

就是这样,现在只把它放在XML中

<com.fitc.views.HtmlTextView
android:id="@+id/html_TV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/example_html" />

用你的Html字符串

<string name="example_html">
<![CDATA[
<b>Author:</b> Mr Donuthead<br/>
<b>Contact:</b> me@donut.com<br/>
<i>Donuts for life </i>
]]>

我也是

如何使用一些漂亮的标记与Kotlin和 -

import org.jetbrains.anko.*
override fun onCreate(savedInstanceState: Bundle?) {
title = "Created with Beautiful Markup"
super.onCreate(savedInstanceState)


verticalLayout {
editText {
hint = buildSpanned {
append("Italic, ", Italic)
append("highlighted", backgroundColor(0xFFFFFF00.toInt()))
append(", Bold", Bold)
}
}
}
}

Created with Beautiful Markup

是的,可以使用SpannedString。如果你正在使用Kotlin,使用core-ktx会变得更容易,因为它提供了领域特定语言(DSL)来做这件事:

    val string: SpannedString = buildSpannedString {
bold {
append("1111")
}
append("Devansh")
}

它提供的更多选项有:

append("Hello There")
bold {
append("bold")
italic {
append("bold and italic")
underline {
append("then some text with underline")
}
}
}

最后,你只需:

textView.text = string

Kotlin中最干净的方法是使用Span

val myTitleText = "Hello World"


val spannable = SpannableString(myTitleText)
spannable.setSpan(
TextAppearanceSpan(context, R.style.myFontMedium),
0,
4,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
tvMytitle.text = spannable

来自:Hello Word

到:你好世界

使用SpannableString是实现这一目标的好方法

我使用了几个函数使其易于应用,我将首先解释每个函数的思想,然后显示代码:

  1. String.getAllIndexOf(pattern: String):这将搜索字符串中的模式,并返回模式开始位置的索引列表。示例:给定字符串"abcdefabc",我调用方法传递"abc"作为搜索模式,该方法应该返回列表:listOf(0, 6)
  2. 是一个接收pattern和一个样式列表的类(如果您希望按顺序将不同的样式应用于相同的模式)
  3. SpannableString.applyStyle(context: Context, vararg patternAndStyles: PatternAndStyles):这将应用样式到给定的模式

现在,在代码上:

  1. < p > 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()
    }
    
  2. 类接收模式和样式

    @Parcelize
    class PatternAndStyles(
    val pattern: String,
    val styles: List<Int>
    ) : Parcelable
    
  3. < p >使用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
    )
    }
    }
    }
    
  4. 到底如何使用它

    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))
    )
    
  5. <李> < p >输出:
    # EYZ0 < / p >

当它被传递给样式到模式"abc"时,两者都被使用 但是在"def"模式中,第二条记录重用了列表中给出的最后一个样式