ChatGPT解决这个技术问题 Extra ChatGPT

TextView 中是否可以有多种样式?

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

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

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

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

开发者指南的 Common Tasks and How to Do Them in Android 包括 Selecting, Highlighting, or Styling Portions of Text

// 获取我们的 EditText 对象。 EditText vw = (EditText)findViewById(R.id.text); // 设置 EditText 的文本。 vw.setText("斜体,突出显示,粗体。"); // 如果这只是一个 TextView,我们可以这样做: // vw.setText("Italic, highlighted, bold.", TextView.BufferType.SPANNABLE); // 强制它使用 Spannable 存储,以便可以附加样式。 // 或者我们可以在 XML 中指定。 // 获取 EditText 的内部文本存储 Spannable str = vw.getText(); // 创建我们的跨度部分,并为每个部分分配格式。 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);

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

如果 TextView 字符串是静态的,您只需将 html 标签添加到字符串资源文件中,它们就会自动应用。例如 其中@string/test 设置为 bold, italic
+1 @greg7gkb!关键词是“静态”。我拉着头发想知道为什么我的一些字符串可以与 一起使用,而有些则不能。那些没有变量的。

C
Community

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

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

有关此方法支持的标签的非官方列表,请参阅 this link 或此问题:Which HTML tags are supported by Android TextView?


@JānisGruzis:也许一种方法是通过存根方法,例如 formatTextWhite(string text),它只是将文本插入以下格式字符串:"<font size="..." color="..." face="...">%s</font>"
@Legend 我用过 " + "Title" + " 但 fgcolor/color(都试过)不是工作...你知道如何使用 html 来做颜色吗
@MuhammadBabar:你可以试试这个:Html.fromHtml("<![CDATA[<font color='#ffff5400'>the html content you already have</font>]]>");?我记得这对我有用。不确定它是否仍然有效。
@Legend 谢谢,但有办法解决它Html.fromHtml("<font color=\"#999999\">转义序列对我有用:)...
为什么这不起作用mBox..setText(Html.fromHtml("<b>" + name + "</b>") + doNotApplyHTML);任何想法谢谢
C
Chad Bingham

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

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

实际上,这让我想到了另一个问题:在不使用 html 类的情况下,最好是有一个带有 html 文本的文本视图,还是有三个具有不同标记设置的文本视图?我假设它显然是第一个但只是想确认它......
我不知道 Html.fromHtml() 支持的完整标签名册 - 你需要查看源代码。内联标记和多个 TextView 小部件应该是正交决策。如果您需要精确放置离散的文本位,请使用多个小部件。如果您需要在小部件中内联标记,请使用内联标记。请记住:Android 中没有 FlowLayout,因此将多个 TextView 串在一起创建一个段落并不是真正实用的 AFAIK。
谢谢你...实际上, 标签有效...所以我会保持简单并使用它...
@TimBoland:您正在通过 monthText + " " + month.getYearLabel() 丢弃您的跨度。见StringUtils.concat()
@TimBoland:如果您不需要单独的部分,您可以一次性完成所有操作:monthYearLabel.setText(Html.fromHtml("<b>"+month.getMonthLabel().toUpperCase()+"</b>&nbsp;"+month.getYearLabel()))
B
Ben

有点离题,但我发现这太有用了,这里就不提了。

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

我没想到这会奏效。但确实如此。

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


通常“假”意味着您访问了查找表上的奇怪值。当我有 R.id.ok 和 R.string.ok 时,我得到了这个,并且不小心使用了 getString(R.id.ok) 而不是正确的 getString(R.string.ok)
这也适用于布局 XML 吗?当我用 "@string/someText" 引用那里的字符串资源时(其中“someText”是在 strings.xml 中定义的资源),我只是将带有所有 HTML 标记的字符串作为“文本”。
我发现这是最好的答案。启发了我。忍不住把我两分钱的答案也放进去了。此处的解决方案 gist.github.com/aegis1980/b138dcb2fd1b2e98aa30 允许您在布局文件中分配,因此您无需以编程方式分配文本资源(并且可以在 Android Studio 中预览!)
S
Suragch

如果你不想使用 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 上将应用两种样式。
对不起,我不清楚。如果您必须指定应用样式的子字符串(通过两个索引),则这不适用于本地化字符串,因为每个语言环境的索引当然会有所不同。
你是对的。如果您支持多种语言,这将不是您想要采取的路线。除非您确定字符串不会改变大小...例如应用程序名称。
我不明白为什么 l10n 不能使用这个解决方案。只需获取单独的字符串,而不仅仅是一个: String firstPart = getString(R.string.line1); String 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(文本,TextView.BufferType.SPANNABLE);
@Rui 因为 l10n 也是位置的。你不能硬编码一个短语中单词的位置,仍然是 l10n。这就是为什么您会在字符串包中看到类似 $1%s is a happy $2%s 的内容。您必须允许重新排列令牌。
C
Community

使用 SpannableString 而不是 html 标记更轻量级。它可以帮助我查看视觉示例,因此这是一个补充答案。

https://i.stack.imgur.com/kFZ3b.png

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

进一步研究

解释 SPAN_EXCLUSIVE_EXCLUSIVE 等 Span 标志的含义

Android Spanned、SpannedString、Spannable、SpannableString 和 CharSequence

跨度类型

此示例最初受 here 启发。


您的帖子比关于 SpannableString 的 Google 文档要好。谢谢
这是否支持来自网络服务的图像。我的要求就是这样。
@MohanRajS SpannableStrings 支持 Unicode 表情符号,但不支持其他图像。
@Suragch 感谢您的及时回复,但我的要求是 jpg、png。唯一要处理的选择是 webview? :(
C
Community

支持的标签列表是:

如果您使用字符串资源,您可以添加一些简单的样式,例如使用 HTML 表示法的粗体或斜体。当前支持的标签有:B(粗体)、I(斜体)、U(下划线)、TT(等宽)、BIG、SMALL、SUP(上标)、SUB(下标)和 STRIKE(删除线)。因此,例如,在 res/values/strings.xml 中,您可以声明: We are so很高兴见到你。

(来自 http://developer.android.com/guide/faq/commontasks.html#selectingtext — Web Archive 链接,<resource> 错字为原文!)

它还表明在简单的情况下实际上并不需要 Html.fromHtml


Html.fromHtml 通常是设置文本样式的更简单方法。此外,此链接已经在原始问题中。
f
farcrats

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

如果字符串的本地化版本重新排序 {0} 和 {1},我认为这不会起作用。如果 startLocation1 < startLocation0,您可能必须更改 startLocation0
P
Phantômaxx

现在不推荐使用 <b> 元素。 <strong> 呈现为 <b>,而 <em> 呈现为 <i>

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

this works fine for me


D
Devansh Maurya

是的,可以使用 SpannedString。如果您使用的是 Kotlin,使用 core-ktx 会变得更容易,因为它提供了一个 domain-specific-language (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

I
Ilya Gazman

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

结果:

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


b
bcorso

如果您希望能够在 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 中正确预览?
A
Andrew Gallasch

如前所述,使用 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


H
Himanshu

我也是

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

https://i.stack.imgur.com/1Gs7H.png


D
Desmond Lua

Spanny 使 SpannableString 更易于使用。

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

C
Clock ZHONG

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


f
forsberg

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

将Strings XML文件中的文本字符串拆分为尽可能多的子字符串(从Android的角度来看是一个单独的字符串),因为您需要不同的样式,所以它可能像:str1,str2,str3(如您的情况),当连接在一起时,就是您使用的整个单个字符串。然后只需遵循“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等等...


M
MontDeska

使用辅助 Spannable 类作为网页底部的 Android String Resources 共享。您可以通过创建CharSquences并为其赋予样式来解决此问题。

但在他们给我们的示例中,仅用于粗体、斜体甚至彩色文本。为了将它们设置在 TextView 中,我需要将多个样式包装在 CharSequence 中。所以对于那个类(我将它命名为 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));

我希望这对你有帮助!


A
AWolfsdorf

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


T
Thiago

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

出自:你好字

致:你好世界


M
M. Massula

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

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

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

现在,在代码上:

getAllIndexOf fun String.getAllIndexOf(pattern: String): List { val allRecordsOfText = mutableListOf() 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() } 接收模式和样式的类 @Parcelize class PatternAndStyles( val pattern: String, val styles: List ) : Parcelable applyStyle fun SpannableString.applyStyle(context: Context, vararg patternAndStyles: PatternAndStyles) { for ( patternAndStyles.toList()) 中的 patternStyle { 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 ) } } } 最后怎么用 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)) ) 输出:

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