Возможно ли иметь несколько стилей внутри 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);
Но это использует явные номера позиций внутри текста. Есть ли более чистый способ сделать это?
20 ответов
В случае, если кто-то задается вопросом, как это сделать, вот один из способов: (Спасибо Марку еще раз!)
mBox = new TextView(context);
mBox.setText(Html.fromHtml("<b>" + title + "</b>" + "<br />" +
"<small>" + description + "</small>" + "<br />" +
"<small>" + DateAdded + "</small>"));
Неофициальный список тегов, поддерживаемых этим методом, см. По этой ссылке или по этому вопросу: Какие теги HTML поддерживаются в Android TextView?
Пытаться Html.fromHtml()
и пометьте свой текст жирными и курсивными HTML-тегами, например:
Spanned text = Html.fromHtml("This mixes <b>bold</b> and <i>italic</i> stuff");
textView.setText(text);
Немного не по теме, но я нашел это слишком полезным, чтобы не упоминать здесь.
Что делать, если мы хотим прочитать текст 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)));
Я не ожидал, что это сработает. Но это так.
Надеюсь, что это полезно для некоторых из вас!
Если вы не хотите использовать 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);
Это более легкий вес, чтобы использовать 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());
Дальнейшее обучение
- Объясните значение флагов Span, таких как SPAN_EXCLUSIVE_EXCLUSIVE
- Спаннинг Android, SpannedString, Spannable, SpannableString и CharSequence
- Типы пролетов
Этот пример был первоначально вдохновлен отсюда.
Список поддерживаемых тегов:
Если вы используете строковый ресурс, вы можете добавить несколько простых стилей, таких как полужирный или курсив, используя 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>
(От https://web.archive.org/web/20121025133239/http://developer.android.com/guide/faq/commontasks.html - ссылка на веб-архив, <resource>
опечатка в оригинале!)
Это также показывает, что Html.fromHtml
не очень нужно в простых случаях.
Теперь <b>
элемент устарел. <strong>
отображается как <b>
, а также <em>
отображается как <i>
,
tv.setText(Html.fromHtml("<strong>bold</strong> and <em>italic</em> "));
Я столкнулся с той же проблемой. Я мог бы использовать 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;
}
Да, это возможно с помощью 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
Вот простой способ сделать это с помощью 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()
);
Результат:
Некоторый текст, выделенный жирным шрифтом
Как указано, использовать 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
Если вы хотите иметь возможность добавлять стилизованный текст в 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
]]>"
/>
Я тоже
Как насчет использования какой-нибудь красивой разметки с Котлиным и Анко -
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)
}
}
}
}
Spanny делает SpannableString проще в использовании.
Spanny spanny = new Spanny("Underline text", new UnderlineSpan())
.append("\nRed text", new ForegroundColorSpan(Color.RED))
.append("\nPlain text");
textView.setText(spanny)
С использованием
SpannableString
это хороший способ добиться этого
Я использую несколько функций, чтобы их было легко применять, я сначала объясню идею каждой, а затем покажу код:
-
String.getAllIndexOf(pattern: String)
: это будет искать шаблон в строке и возвращать список индексов, где шаблон начинается. Пример: учитывая строку"abcdefabc"
и я вызываю метод, передавая в качестве искомого шаблона, метод должен возвращать список:listOf(0, 6)
- Это класс, чтобы получить
pattern
и список стилей (на случай, если вы хотите последовательно применить разные стили к одному и тому же шаблону) -
SpannableString.applyStyle(context: Context, vararg patternAndStyles: PatternAndStyles)
: Это применит стили к заданным шаблонам.
Теперь по коду:
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() }
Класс для получения шаблона и стилей
@Parcelize class PatternAndStyles( val pattern: String, val styles: List<Int> ) : Parcelable
применить стиль
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 ) } } }
Как это использовать в конце концов
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"
вторая запись повторно использовала последний стиль, указанный в списке
Самый чистый способ в Котлине - использовать 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
Кому: Hello World
Как сказал Джон, для меня это лучшее решение, и вам не нужно устанавливать какой-либо текст во время выполнения, используйте только этот пользовательский класс 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>
]]>
Это может быть так же просто, как использовать метод String length ():
Разбейте текстовую строку в XML-файле Strings на столько подстрок (отдельных строк с точки зрения Android), сколько вам нужно разных стилей, поэтому это может выглядеть так: str1, str2, str3 (как в вашем случае), которые при объединении представляют собой целую строку, которую вы используете.
А затем просто следуйте методу "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 и так далее...
Использование вспомогательного класса 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));
Я надеюсь, что это поможет вам!
Фактически, кроме объекта Html, вы также можете использовать классы типов Spannable, например, TextAppearanceSpan или TypefaceSpan и SpannableString. Класс HTML также использует эти механизмы. Но с классами Spannable вы получаете больше свободы.