115

A ListView in my application has many string elements like name, experience, date of joining, etc. I just want to make name bold. All the string elements will be in a single TextView.

my XML:

<ImageView
    android:id="@+id/logo"
    android:layout_width="55dp"
    android:layout_height="55dp"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    android:layout_marginTop="15dp" >
</ImageView>

<TextView
    android:id="@+id/label"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_toRightOf="@id/logo"
    android:padding="5dp"
    android:textSize="12dp" >
</TextView>

My code to set the TextView of the ListView item:

holder.text.setText(name + "\n" + expirience + " " + dateOfJoininf);
Innat
  • 16,113
  • 6
  • 53
  • 101
Housefly
  • 4,324
  • 11
  • 43
  • 70

10 Answers10

261

Let's say you have a TextView called etx. You would then use the following code:

final SpannableStringBuilder sb = new SpannableStringBuilder("HELLOO");
      
final StyleSpan bss = new StyleSpan(android.graphics.Typeface.BOLD); // Span to make text bold
final StyleSpan iss = new StyleSpan(android.graphics.Typeface.ITALIC); //Span to make text italic
sb.setSpan(bss, 0, 4, Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make first 4 characters Bold 
sb.setSpan(iss, 4, 6, Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make last 2 characters Italic

etx.setText(sb);

Innat
  • 16,113
  • 6
  • 53
  • 101
Imran Rana
  • 11,899
  • 7
  • 45
  • 51
31

Based on Imran Rana's answer, here is a generic, reusable method if you need to apply StyleSpans to several TextViews, with support for multiple languages (where indices are variable):

void setTextWithSpan(TextView textView, String text, String spanText, StyleSpan style) {
    SpannableStringBuilder sb = new SpannableStringBuilder(text);
    int start = text.indexOf(spanText);
    int end = start + spanText.length();
    sb.setSpan(style, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
    textView.setText(sb);
}

Use it in an Activity like so:

@Override
protected void onCreate(Bundle savedInstanceState) {
    // ...

    StyleSpan boldStyle = new StyleSpan(Typeface.BOLD);
    setTextWithSpan((TextView) findViewById(R.id.welcome_text),
        getString(R.string.welcome_text),
        getString(R.string.welcome_text_bold),
        boldStyle);

    // ...
}

strings.xml

<string name="welcome_text">Welcome to CompanyName</string>
<string name="welcome_text_bold">CompanyName</string>

Result:

Welcome to CompanyName

friederbluemle
  • 33,549
  • 14
  • 108
  • 109
29

You can do it using Kotlin and buildSpannedString extension function from core-ktx

 holder.textView.text = buildSpannedString {
        bold { append("$name\n") }
        append("$experience $dateOfJoining")
 }
Dmitrii Leonov
  • 1,331
  • 1
  • 15
  • 25
14

The answers provided here are correct, but can't be called in a loop because the StyleSpan object is a single contiguous span (not a style that can be applied to multiple spans). Calling setSpan multiple times with the same bold StyleSpan would create one bold span and just move it around in the parent span.

In my case (displaying search results), I needed to make all instances of all the search keywords appear bold. This is what I did:

private static SpannableStringBuilder emboldenKeywords(final String text,
                                                       final String[] searchKeywords) {
    // searching in the lower case text to make sure we catch all cases
    final String loweredMasterText = text.toLowerCase(Locale.ENGLISH);
    final SpannableStringBuilder span = new SpannableStringBuilder(text);

    // for each keyword
    for (final String keyword : searchKeywords) {
        // lower the keyword to catch both lower and upper case chars
        final String loweredKeyword = keyword.toLowerCase(Locale.ENGLISH);

        // start at the beginning of the master text
        int offset = 0;
        int start;
        final int len = keyword.length(); // let's calculate this outside the 'while'

        while ((start = loweredMasterText.indexOf(loweredKeyword, offset)) >= 0) {
            // make it bold
            span.setSpan(new StyleSpan(Typeface.BOLD), start, start+len, SPAN_INCLUSIVE_INCLUSIVE);
            // move your offset pointer 
            offset = start + len;
        }
    }

    // put it in your TextView and smoke it!
    return span;
}

Keep in mind that the code above isn't smart enough to skip double-bolding if one keyword is a substring of the other. For example, if you search for "Fish fi" inside "Fishes in the fisty Sea" it will make the "fish" bold once and then the "fi" portion. The good thing is that while inefficient and a bit undesirable, it won't have a visual drawback as your displayed result will still look like

Fishes in the fisty Sea

copolii
  • 14,208
  • 10
  • 51
  • 80
  • hey man can you please see this https://stackoverflow.com/questions/59947482/android-how-to-adjust-index-in-a-string-after-removing-some-characters – Pemba Tamang Jan 28 '20 at 11:39
7

if you don't know exactly the length of the text before the text portion that you want to make Bold, or even you don't know the length of the text to be Bold, you can easily use HTML tags like the following:

yourTextView.setText(Html.fromHtml("text before " + "<font><b>" + "text to be Bold" + "</b></font>" + " text after"));
Muhammed Refaat
  • 8,914
  • 14
  • 83
  • 118
3
<string name="My_Name">Given name is <b>Not Right</b>Please try again </string>

use "b" tag in string.xml file.
also for Italic "i" and Underline "u".

1

I recommend to use strings.xml file with CDATA

<string name="mystring"><![CDATA[ <b>Hello</b> <i>World</i> ]]></string>

Then in the java file :

TextView myTextView = (TextView) this.findViewById(R.id.myTextView);
myTextView.setText(Html.fromHtml( getResources().getString(R.string.mystring) ));
sonique
  • 4,539
  • 2
  • 30
  • 39
1

Extending frieder's answer to support case and diacritics insensitivity.

public static String stripDiacritics(String s) {
        s = Normalizer.normalize(s, Normalizer.Form.NFD);
        s = s.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "");
        return s;
}

public static void setTextWithSpan(TextView textView, String text, String spanText, StyleSpan style, boolean caseDiacriticsInsensitive) {
        SpannableStringBuilder sb = new SpannableStringBuilder(text);
        int start;
        if (caseDiacriticsInsensitive) {
            start = stripDiacritics(text).toLowerCase(Locale.US).indexOf(stripDiacritics(spanText).toLowerCase(Locale.US));
        } else {
            start = text.indexOf(spanText);
        }
        int end = start + spanText.length();
        if (start > -1)
            sb.setSpan(style, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        textView.setText(sb);
    }
luky
  • 2,263
  • 3
  • 22
  • 40
1

If you are using the @ srings / your_string annotation, access the strings.xml file and use the <b></b> tag in the part of the text you want.

Example:

    <string><b>Bold Text</b><i>italic</i>Normal Text</string>
0

To better support translations and remove any dependency on length of the string or particular index, you should use android.text.Annotation in you string defined strings.xml.

In your particular case, you can create a string like below

<string name="bold_name_experience_text"><annotation type="bold">name</annotation> \nexpirience dateOfJoininf</string>

or if you want to substitute these in runtime, you can create a string as follow

<string name="bold_name_experience_text"><annotation type="bold">name</annotation> \n%d %s</string>

You must apply this bold_name_experience_text in your text view label. These annotation class spans get added to your string and then you can iterate on them to apply the bold span.

You can refer to my SO answer which shows the Kotlin code to iterate through these spans and apply the bold span

Remember all the above answers has one of the following flows:

  1. They are using some hard-coded index logic which may crash or give wrong results in some other language
  2. They are using hardcode string in Java code which will result in lots of complicated logic to maintain internalisation
  3. Some used Html.fromHtml which can be acceptable answer depending on the use-case. As Html.fromHtml doesn't always work for all types of HTML attributes for example there is not support of click span. Also depending on OEM you might get different rendered TextView
AndroidEngineX
  • 975
  • 1
  • 9
  • 22