SpannableStringBuilder для створення рядка з декількома розмірами шрифтів / тексту тощо Приклад?


81

Мені потрібно створити рядок, розміщений у TextView, який відображатиме такий рядок:

Перша частина Не сміливий СМІЛИЙ відпочинок не жирний

Тож я хочу знати, як я міг SpannableStringBuilderце зробити?

Для цього я міг би використати три TextEdit, але хотів би використати найкраще рішення.

Відповіді:


88
First Part Not Bold   BOLD  rest not bold

Ви можете зробити це як за пропозицією @Rajesh, так і цим способом.

String normalBefore= "First Part Not Bold ";
String normalBOLD=  "BOLD ";
String normalAfter= "rest not bold";
String finalString= normalBefore+normalBOLD+normalAfter;
Spannable sb = new SpannableString( finalString );
sb.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), finalString.indexOf(normalBOLD)+ normalBOLD.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); //bold
sb.setSpan(new AbsoluteSizeSpan(intSize), finalString.indexOf(normalBOLD)+ normalBOLD.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);//resize size

щоб показати це в TextView

textview.setText(sb,  TextView.BufferType.SPANNABLE);

1
Також як я можу встановити розмір шрифту, а також для напівжирного шрифта? тож я вказую, що це жирний шрифт, і кажу шрифт 20?
Code Droid

7
Також, будь ласка, оновіть індекс до (finalString.indexOf (normalBOLD), finalString.indexOf (normalBOLD) + normalBOLD.lenth ())
Code Droid

2
Окрім того, що сказав CodeDroid, інша проблема полягає в тому, що метод indexOf може вловити повторене слово і залишити його endперед вашим startдля IndexOutOfBoundsException. Тож краще відформатувати підрядки, а потім скласти їх разом.
Noumenon

@hotveryspicy Я підозрюю, що "finalString.indexOf (normalBOLD)" ця частина - на відміну від зовнішнього вигляду - ефективна через інтернізацію рядків .. чи не так?
Paul Brewczynski

ПРИМІТКА: android:textAllCaps="true"розірве SpannableString
Хтось десь

80

Прийнята відповідь чудова (і я підтримав її), але вона не використовує SpannableStringBuilder як запитувач. Як у мене був випадок, коли Builder мав найбільший сенс, ось код для цього (з бонусним використанням також зміни кольору тексту, якщо це корисно для інших). Зверніть увагу, що ви також можете надати початковий рядок конструктору SpannableStringBuilder, але я встановив його тут, щоб використовувати "додати", щоб було зрозуміло, що ви можете додати багато до потрібного "жирного" тексту, а потім просто записати початок, як показано. Я підозрюю, що це також швидший код, ніж прийнята відповідь.

SpannableStringBuilder longDescription = new SpannableStringBuilder();
longDescription.append("First Part Not Bold ");
int start = longDescription.length();
longDescription.append("BOLD");
longDescription.setSpan(new ForegroundColorSpan(0xFFCC5500), start, longDescription.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
longDescription.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), start, longDescription.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
longDescription.append(" rest not bold");

2
Крім того, отримавши початкову позицію жирної частини тексту перед тим, як додати її, вам не доведеться турбуватися про повторювані випадки слова, яке має бути напівжирним.
Qw4z1

57

Якщо ви використовуєте Kotlin, ви можете зробити наступне за допомогою бібліотеки android-ktx

val s = SpannableStringBuilder()
        .append("First Part Not Bold ")
        .bold { append("BOLD") } 
        .append("Rest not bold")

Це boldфункція розширення SpannableStringBuilder. Ви можете переглянути документацію тут для переліку операцій, якими ви можете скористатися.

Інший приклад:

val s = SpannableStringBuilder()
            .color(green, { append("Green text ") })
            .append("Normal text ")
            .scale(0.5, { append("Text at half size " })
            .backgroundColor(green, { append("Background green") })

Де greenрозрізнений колір RGB.

Можна навіть вкласти прольоти, щоб ви отримали вбудований DSL:

bold { underline { italic { append("Bold and underlined") } } }

Для роботи вам потрібно наступне на рівні модуля програми build.gradle:

repositories {
    google()
}

dependencies {
    implementation "androidx.core:core-ktx:1.2.0"
}

3
Безумовно, найкращий варіант, якщо ви можете використовувати Kotlin. DSL робить його набагато приємнішим API.
Baptiste Candellier

1
Як це працює з LiveData <String>? Здається, я не можу змусити його застосувати ці зміни, хоча все компілюється і виглядає нормально?
слухайтеся

42

З API 21 SpannableStringBuilder включає простий спосіб зробити це. Ось приклад рішення:

SpannableStringBuilder builder= new SpannableStringBuilder();
StyleSpan boldSpan = new StyleSpan(android.graphics.Typeface.BOLD);
builder.append("First Part Not Bold ")
              .append("BOLD ", boldSpan, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
              .append("rest not bold");

Оскільки є велика ймовірність, що ви не підтримуєте API 21, лише ви можете продублювати код із цього методу:

public SpannableStringBuilder append(CharSequence text, Object what, int flags) {
        int start = length();
        append(text);
        setSpan(what, start, length(), flags);
        return this;
}

7

Використовуйте HTML-код у TextView, використовуючи Htmlклас:

Spanned styledText = Html.fromHtml("First Part Not Bold <b>BOLD</b> rest not bold");
textView.setText(styledText);

20
Майте на увазі, що це дійсно повільно і, швидше за все, ви пропустите кадри, якщо це буде зроблено під час прокрутки.
Філ Кулак

6

Цей код повинен мати жирний шрифт усього, що входить до тегу html жирного шрифту. І він також видаляє тег, так що відображається лише вміст усередині.

        SpannableStringBuilder sb = new SpannableStringBuilder("this is <b>bold</b> and this is <b>bold too</b>  and this is <b>bold too, again</b>.");

        Pattern p = Pattern.compile("<b>.*?</b>", Pattern.CASE_INSENSITIVE);            
        boolean stop = false;
        while (!stop)
        {
            Matcher m = p.matcher(sb.toString());
            if (m.find()) {
                sb.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), m.start(), m.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
                sb.delete(m.end()-4, m.end());
                sb.delete(m.start(), m.start() + 3);
            }
            else
                stop = true;
        }

Цей код також можна адаптувати для інших тегів стилю html, таких як верхній індекс (тег sup) тощо.

        SpannableStringBuilder sb = new SpannableStringBuilder("text has <sup>superscript</sup> tag");

        Pattern p = Pattern.compile("<sup>.*?</sup>", Pattern.CASE_INSENSITIVE);            
        boolean stop = false;
        while (!stop)
        {
            Matcher m = p.matcher(sb.toString());
            if (m.find()) {
                sb.setSpan(new SuperscriptSpan(), m.start(), m.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
                sb.delete(m.end()-6, m.end());
                sb.delete(m.start(), m.start() + 5);
            }
            else
                stop = true;
        }

Щоб встановити колір, просто використовуйте ForegroundColorSpan із setSpan.

sb.setSpan(new ForegroundColorSpan(Color.rgb(255, 0, 0)), m.start(), m.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);

Сподіваюся, це допоможе.


Чудова відповідь, ти врятував мій день! Я замінив шаблон на val p = Pattern.compile ("<b> ([^ <] *) </b>", Pattern.MULTILINE або Pattern.DOTALL), оскільки використання вашого шаблону, якщо у вас є лише тег старту текст напівжирним шрифтом. Дякую
AndroidRuntimeException

4

Отже, я знаю, що це було вирішено, і навіть за запитом SpannableStringBuilder, але в тому випадку, якщо ви хочете побудувати рядок більш динамічно, я припустив, що поставлю це.

// Stuff needed
TextView DataTextView = (TextView)rootView.findViewById(R.id.DataView);
String Fields[] = {...database column names as strings... "x","y"};

String DataString = new String();   

int start,stop;     // Start and Stop of formatting

// Final Result
SpannableStringBuilder coloredString = new SpannableStringBuilder(); 

SpannableString temp;       // Small segment of colored string
for (int i =0; i < Fields.length; i++)
{
    if (database_result.containsKey(Fields[i]))  // Be sure a field exists in the ContentValues
    {
            DataString = Fields[i]+": ";
        start = DataString.length();
        DataString = DataString+ +database_result.getAsInteger(Fields[i])+" ";
        stop= DataString.length();
        temp = new SpannableString(DataString);
        temp.setSpan(new ForegroundColorSpan(Color.WHITE),start, stop, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        coloredString.append(temp);
    }   
}
DataTextView.setText(coloredString);

data_result - це тип ContentValues, який я побудував із повернутого типу курсора запиту SQL. Єдиною проблемою, яка у мене була з цим, спочатку було лише ColorSpaning першого сегмента. Здається, вам потрібно оголосити новий ForegroundColorSpan кожного разу, коли ви хочете використовувати один (або будь-який інший діапазон) у циклі.


3

Для цього ми також можемо використовувати SpannableStringBuilder з TextAppearanceSpan . Виконайте наступні кроки, щоб реалізувати це.

  1. Створіть стиль в styles.xml.

<style name="BoldStyle">
   <!-- Can add other styling attributes -->
   <item name="android:textStyle">bold</item>
   ......
</style>

  1. Використовуйте код нижче.
SpannableStringBuilder builder = новий SpannableStringBuilder ("Перша частина не напівжирний НАПІЛЬНИЙ відпочинок не жирний");
builder.setSpan (new TextAppearanceSpan (this, R.style.BoldStyle), 20, 24, 0);
((TextView) findViewById (R.id.tv7)). SetText (конструктор);

Це воно. Сподіваюся, це комусь допоможе.


1

Чому ви використовуєте SpannableStringBuilder, коли ви можете використовувати SpannableBuilder ?? ( https://gist.github.com/qtyq/90f9b4894069a8b3676c )

SpannableString ss = SpannableBuilder.init("First Part Not Bold BOLD rest not bold")
                                     .makeBold("BOLD")
                                     .create()

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