Чи можливо мати декілька стилів у TextView?


547

Чи можливо встановити кілька стилів для різних фрагментів тексту всередині 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);

Але для цього використовуються чіткі позиційні номери всередині тексту. Чи є чистіший спосіб зробити це?


6
Якщо рядок TextView статичний, ви можете просто додати теги html <b>, <i> та <u> у файл ресурсу рядків, і вони автоматично будуть застосовані. Наприклад, <TextView android: text = "@ string / test" /> де для параметра @ string / test встановлено <string> <b> жирний </b>, <i>italic</> </string>
greg7gkb

2
+1 @ greg7gkb! Ключове слово - "статичний". Я витягнув волосся, цікавившись, чому деякі мої пасма працювали з <b>, а деякі - ні. Ті, які не мали в них змінних.
Скотт Біггс

Відповіді:


694

У разі, коли хтось задається питанням, як це зробити, ось один із способів: (Ще раз дякую Марку!)

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

Для неофіційного списку тегів, що підтримуються цим методом, перейдіть за цим посиланням або цим питанням: Які теги HTML підтримує Android TextView?


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\">евакуації для мене:) ...
Мухаммед Бабар

2
чому це не працює mBox..setText(Html.fromHtml("<b>" + name + "</b>") + doNotApplyHTML);ідея спасибі
Shan Xeeshi

216

Спробуйте Html.fromHtml()та позначте свій текст жирними та курсивними HTML-тегами, наприклад:

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

1
Насправді це мене викликає ще одне запитання: чи краще мати один перегляд тексту з HTML-текстом всередині нього або три перегляди тексту з різними налаштуваннями розмітки без використання html-класу? Я припускаю, що його, очевидно, перший, але просто хотів підтвердити це ...
Легенда

2
Я поняття не маю, що підтримує повний реєстр тегів, який підтримує Html.fromHtml () - вам потрібно буде переглянути вихідний код. Вбудована розмітка та декілька віджетів TextView повинні бути ортогональними рішеннями. Використовуйте кілька віджетів, якщо вам потрібно точно розмістити дискретні шматочки тексту. Використовуйте вбудовану розмітку, якщо вам, гм, потрібна розмітка inline у ​​віджеті. Пам'ятайте: FlowLayout в Android не існує, тому з'єднання декількох TextView, щоб створити абзац, не є справді практичним AFAIK.
CommonsWare

1
Дякую за це ... Насправді тег <small> спрацював ... Тож я буду простою та просто використовувати його ...
Легенда

1
@TimBoland: Ви викидаєте свої прольоти через monthText + " " + month.getYearLabel(). Див StringUtils.concat().
CommonsWare

2
@TimBoland: Ви можете зробити це все в одному:, monthYearLabel.setText(Html.fromHtml("<b>"+month.getMonthLabel().toUpperCase()+"</b>&nbsp;"+month.getYearLabel()))якщо вам не потрібні окремі фрагменти.
CommonsWare

191

Трохи поза темою, але я вважаю це занадто корисним, щоб не згадувати тут.

Що робити, якщо ми хотіли б прочитати текст Html з ресурсу string.xml і таким чином полегшити його локалізацію. 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)
Джо Плант

Чи працює це також у макеті-XML? Коли я посилаюсь на "@string/someText"рядовий ресурс з (де "someText" - це ресурс, визначений у strings.xml), я просто отримую рядок із усіма тегами HTML як "текст".
Gee.E

3
Я вважав це найкращою відповіддю. Надихнули мене. Не могло не допомогти, але і мої два центи варті відповіді. Рішення тут gist.github.com/aegis1980/b138dcb2fd1b2e98aa30 дозволяє призначати в файл макета , так що вам не потрібно призначити текстовий ресурс програмно (і може переглядати в Android Studio!)
Jon

127

Якщо вам не подобається використовувати 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, над якою ви можете працювати. Ви побачите, що в одному текстовому перегляді будуть застосовані два стилі.
Кент Андерсен

вибачте, що я не зрозумів якщо вам потрібно вказати підрядку (через два індекси), куди слід застосувати стиль, це не працює для локалізованих рядків, тому що індекси, звичайно, будуть різними для кожної локалі.
Джефрі Блатман

Ви праві. Якщо ви підтримуєте кілька мов, це не буде маршрут, який ви хотіли б пройти. Якщо ви не були впевнені, рядок не змінить розміри ..., наприклад, назва програми.
Кент Андерсен

9
Я не бачу, чому l10n не може працювати з цим рішенням. Просто отримайте окремі рядки замість одного: String firstPart = getString (R.string.line1); Рядок secondPart = getString (R.string.line2); Текст, що розгортається = новий SpannableString (firstPart + secondPart); text.setSpan (новий ForegroundColorSpan (Color.parseColor (TEXT_COLOR_FOR_FIRST_PART)), 0, firstPart.length (), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); myTextView.setText (текст, TextView.BufferType.SPANNABLE);
Руй

5
@Rui, тому що l10n також є позиційним. ви не можете жорстко кодувати положення слів у фразі і все одно бути l10n. саме тому ви бачите такі речі, як $1%s is a happy $2%sу рядках. ви повинні дозволити перестановку жетонів.
Джефрі Блатман

60

Більш легка вага використовувати 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
Ваше повідомлення краще, ніж Google Документація про SpannableString .. Дякую
MBH

Це підтримувало зображення, які з веб-сервісу. Моя вимога така.
MohanRaj S

@MohanRajS SpannableStrings підтримують смайли Unicode, але не інші зображення.
Сурагч

@Suragch дякую за швидку відповідь, але моя сторона вимагає jpg, png. Єдиним вибором для обробки є веб-перегляд? :(
MohanRaj S

43

Список підтримуваних тегів:

Якщо ви використовуєте рядовий ресурс, ви можете додати простий стиль, наприклад, напівжирний або курсив, використовуючи позначення HTML. В даний час підтримуються теги: B(виділені жирним шрифтом), I(курсив), U(підкреслення), TT(моно) BIG, SMALL, SUP(верхній індекс), 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 - посилання веб-архіву, <resource>помилка друку в оригіналі!)

Це також показує, що Html.fromHtmlнасправді це не потрібно в простих випадках.


Html.fromHtml - це найпростіший спосіб стилізації тексту. Також це посилання вже є в оригінальному питанні.
інтригації

17

Я зіткнувся з тією ж проблемою. Я можу використовувати fromHtml, але я зараз андроїд, а не Інтернет, тому я вирішив спробувати це. Мені доводиться локалізувати це, хоча, тому я дав йому знімок, використовуючи концепцію заміни рядків. Я встановив стиль 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}. Можливо, вам доведеться змінити startLocation0, якщо startLocation1 є <startLocation0
JonathanC


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

7

Як зазначено, використовуйте 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


3

Я також

Як щодо використання красивої розмітки з Котліном та Анко -

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 togather. Html клас також використовує ці механізми. Але з класами типу Spannable у вас більше свободи.


0

Це може бути таким же простим, як використання методу length () String:

  1. Розділіть текстовий рядок у XML-файлі Strings на стільки підрядків (окремі рядки з точки зору 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 Class як 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>
]]>

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.