Возможно ли иметь несколько стилей внутри 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> курсив </ i> </ 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> "+" Title "+" </ big> </ b> </ font>, но fgcolor / color (оба пробовали) не работает ... знаете ли вы, как сделать цвет, используя HTML
Мухаммед Бабар
@MuhammadBabar: вы можете попробовать это Html.fromHtml("<![CDATA[<font color='#ffff5400'>the html content you already have</font>]]>");? Я помню, это сработало некоторое время назад для меня. Не уверен, что это все еще работает.
Легенда
1
@ Легенда, спасибо, но у меня есть способ обойти побеги, сработавшие 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);
CommonsWare
источник
1
На самом деле, это приводит меня к другому вопросу: лучше ли иметь одно текстовое представление с html-текстом внутри или три текстовых представления с разными настройками разметки без использования html-класса? Я предполагаю, что это, очевидно, первый, но просто хотел подтвердить это ...
Легенда
2
Я понятия не имею, что полный список тегов, которые поддерживает Html.fromHtml () - вам нужно будет взглянуть на исходный код. Встроенная разметка и несколько виджетов TextView должны быть ортогональными решениями. Используйте несколько виджетов, если вам нужно точное размещение отдельных битов текста. Используйте встроенную разметку, если вам нужна встроенная разметка в виджете. Помните: в Android нет FlowLayout, поэтому объединение нескольких TextViews для создания абзаца не является действительно практичным 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
Обычно «ложь» подразумевает, что вы обратились к странному значению в таблице поиска. Я получил это, когда у меня были 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); String secondPart = getString (R.string.line2); Spannable text = new 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

Гораздо проще использовать 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());

Дальнейшее изучение

Этот пример был первоначально вдохновлен отсюда .

Suragch
источник
5
Ваш пост лучше, чем Google Документация о SpannableString .. Спасибо
MBH
Поддерживает ли это изображения, которые из веб-службы. Мое требование, как это.
МоханРадж С
@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, но теперь я 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;
}
farcrats
источник
Я не думаю, что это будет работать, если локализованная версия переупорядочивает строки {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
    ]]>"
/>
bcorso
источник
Правильно ли он в 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)
            }
        }
    }
}

Создано с красивой разметкой

Himanshu
источник
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 вы получаете больше свободы.

Часы ZHONG
источник
0

Это может быть так же просто, как использование метода length () строки:

  1. Разбейте текстовую строку в XML-файле Strings на столько подстрок (отдельных строк с точки зрения Android), сколько вам нужно разных стилей, поэтому это может выглядеть так: str1, str2, str3 (как в вашем случае), которые при объединении представляют собой целую строку, которую вы используете.

  2. А затем просто следуйте методу «Span», так же, как вы представили свой код - но вместо одной строки объедините все подстроки, объединяя их в одну, каждая со своим собственным стилем.

Вы по-прежнему используете числа, но не напрямую - они больше не принимают жестко заданную форму (как в вашем коде) сейчас, но они заменяются комбинированными методами length () (обратите внимание на две звезды перед суффиксом и суффиксом над ним). 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 в нижней части веб-страницы. Вы можете подойти к этому, создав 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));

Я надеюсь, что это поможет вам!

MontDeska
источник
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>
]]>

AWolfsdorf
источник