TextView内可以有多种样式吗?


547

是否可以在TextView中为不同的文本设置多种样式?

例如,我将文本设置如下:

tv.setText(line1 + "\n" + line2 + "\n" + word1 + "\t" + word2 + "\t" + word3);

每个文本元素是否可以具有不同的样式?例如,line1粗体,word1斜体等。

开发人员指南的“ 常见任务”和“如何在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);

但这在文本中使用了明确的位置编号。有没有更清洁的方法可以做到这一点?


6
如果TextView字符串是静态的,则只需将html <b>,<i>和<u>标记添加到字符串资源文件中,它们将被自动应用。例如<TextView的机器人:文本= “@串/测试”/>其中@串/测试设置为<字符串> <b>粗体</ B>,<I>斜体</ i>的</字符串>
greg7gkb

2
+1 @ greg7gkb!关键字是“静态”。我正在拔头发,想知道为什么我的某些琴弦可以与<b>一起使用,而有些琴弦却不能。没有变量的变量。
Scott Biggs

Answers:


694

万一有人想知道如何做,这是一种方法:(再次感谢Mark!)

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

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


1
@JānisGruzis:也许一种方法是通过存根方法,例如,formatTextWhite(string text)将文本插入以下格式字符串中:"<font size="..." color="..." face="...">%s</font>"
联想

@Legend我使用了<font fgcolor ='#ffff5400'> <b> <big>“ +”标题“ +” </ big> </ b> </ font>,但是fgcolor / color(都尝试过)没有工作......你知道该怎么做使用HTML颜色嘛
穆罕默德·巴巴尔

@MuhammadBabar:你可以试试这个Html.fromHtml("<![CDATA[<font color='#ffff5400'>the html content you already have</font>]]>");吗?我记得有一段时间对我有用。不知道它是否仍然有效。
传奇

1
@Legend谢谢,但是有一个解决方法Html.fromHtml("<font color=\"#999999\">,逃生序列对我有用:)...
Muhammad Babar 2013年

2
为什么这行不通, mBox..setText(Html.fromHtml("<b>" + name + "</b>") + doNotApplyHTML);谢谢
Shan Xeeshi 2014年

216

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

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

1
实际上,这使我想到了另一个问题:在其中使用一个带html文本的textview还是在不使用html类的情况下使用具有不同标记设置的三个textview更好?我假设它显然是第一个,但只是想确认一下...
传奇

2
我不知道Html.fromHtml()支持的完整标签列表是什么-您需要查看源代码。内联标记和多个TextView小部件应为正交决策。如果需要精确放置离散的文本位,请使用多个小部件。如果您在小部件中需要内联标记,请使用内联标记。请记住:Android中没有FlowLayout,因此将多个TextView串在一起以创建段落并不是真正实用的AFAIK。
CommonsWare,2009年

1
谢谢。。。实际上,<small>标记起作用了。所以我将其简化并使用它
Legend

1
@TimBoland:您通过扔掉了跨度monthText + " " + month.getYearLabel()。请参阅StringUtils.concat()
CommonsWare

2
@TimBoland:monthYearLabel.setText(Html.fromHtml("<b>"+month.getMonthLabel().toUpperCase()+"</b>&nbsp;"+month.getYearLabel()))如果不需要单独的部分,则可以全部完成:。
CommonsWare,2016年

191

有点题外话,但我发现它太有用了,这里不予提及。

如果我们想从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))); 

我没想到这行得通。但是确实如此。

希望对您中的某些人有用!


2
通常,“ false”表示您访问了查询表中的一个怪异值。当我同时拥有R.id.ok和R.string.ok时得到了这个,并且不小心使用了getString(R.id.ok)而不是正确的getString(R.string.ok)
Joe Plante

这也可以在layout-XML中使用吗?当我在其中引用字符串资源时"@string/someText"(其中“ someText”是在strings.xml中定义的资源),我只是将带有所有HTML标记的字符串作为“文本”。
Gee.E

3
我找到了最佳答案。启发了我。我也忍不住要给我两美分的答复。这里的解决方案gist.github.com/aegis1980/b138dcb2fd1b2e98aa30允许您在布局文件中进行分配,因此您无需以编程方式分配文本资源(并且可以在Android Studio中预览!)
2015年

126

如果您不想使用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);

将其复制/粘贴到您可能正在使用的简单android应用程序中。您将看到在单个textview上将应用两种样式。
肯特·安德森

对不起,我不清楚。如果必须(通过两个索引)指定应用样式的位置的子字符串,则该方法不适用于本地化的字符串,因为对于每个语言环境,索引当然都会有所不同。
Jeffrey Blattman,2012年

你是对的。如果您支持多种语言,那将不是您要采用的路线。除非您确定该字符串不会更改大小,例如应用程序名称。
肯特·安德森

9
我不明白为什么l10n无法使用此解决方案。只需获取单独的字符串而不是一个字符串即可:String firstPart = getString(R.string.line1); 字符串secondPart = getString(R.string.line2); 可扩展文本=新的SpannableString(firstPart + secondPart); text.setSpan(new ForegroundColorSpan(Color.parseColor(TEXT_COLOR_FOR_FIRST_PART)),0,firstPart.length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); myTextView.setText(text,TextView.BufferType.SPANNABLE);

5
@Rui,因为l10n也是位置。您无法对词组中单词的位置进行硬编码,而仍然是l10n。这就是为什么您看到诸如$1%s is a happy $2%s字符串捆绑之类的东西的原因。您必须允许重新排列令牌。
杰弗里·布拉特曼

60

使用a SpannableString代替html标记更轻巧。它可以帮助我查看视觉示例,因此这里是一个补充性答案。

在此处输入图片说明

这是一个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());

进一步研究

这个例子最初是从这里得到启发的。


5
您的帖子比有关SpannableString的Google文档更好。.谢谢
MBH

是否支持来自Web服务的图像。我的要求就是这样。
MohanRaj S

@MohanRajS SpannableStrings支持Unicode表情符号,但不支持其他图像。
Suragch

@Suragch感谢您的及时答复,但我的要求是jpg,png。唯一可以处理的选择是webview?:(
MohanRaj S

43

支持的标签列表为:

如果使用字符串资源,则可以添加一些简单的样式,例如使用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在简单情况下并不需要。


Html.fromHtml通常是一种更简单的样式文本方式。另外,此链接已经在原始问题中。
复杂

17

我遇到了同样的问题。我可以使用fromHtml,但是我现在是android,而不是网络,因此我决定尝试一下。我确实必须对此进行本地化,因此我使用字符串替换概念给了它一个镜头。我将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;
}

我认为如果字符串的本地化版本重新排列{0}和{1},这将不起作用。如果startLocation1为<startLocation0
JonathanC

14

现在<b>已弃用该元素。<strong>呈现为<b>,并<em>呈现为<i>

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

这对我来说很好


8

这是使用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()
    );

结果:

一些粗体文本 一些斜体文本


7

如果您希望能够在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
    ]]>"
/>

然后可以在Android Studio中正确预览吗?
JPM


3

我也是

如何在Kotlin和Anko中使用一些漂亮的标记-

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)
            }
        }
    }
}

用美丽的标记创建


2

是的,可以使用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

0

实际上,除了Html对象外,您还可以使用Spannable类型类,例如TextAppearanceSpan或TypefaceSpan和SpannableString。HTML类也使用这些机制。但是,有了Spannable类型类,您将拥有更多的自由。


0

它可能与利用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等等...


0

在网页底部使用辅助Spannable类作为Android String Resources共享。您可以通过创建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));

希望对您有所帮助!


0

Spanny使SpannableString易于使用。

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

0

正如乔恩所说,对我来说这是最好的解决方案,您不需要在运行时设置任何文本,只需使用此自定义类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>
]]>

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.