Search code examples
androidtextviewspannablestring

Setting a spannable string not working on a simple Textview


I cannot for the life of me understand why this simple code to set a spannable string is not working on this textview. The method below adds a "Today" marker, which should be in green, before the text displaying the date if the date is the current day.

private void setTimeTextView(String timeString) {

    Calendar c = Calendar.getInstance();

    String todaysDateString = ApiContentFormattingUtil.getFullDateFormat(c.getTime());
    if (timeString.equals(todaysDateString)){
        String todayText = getResources().getString(R.string.today_marker);

        Spannable timeSpannable = new SpannableString(todayText + timeString);
        timeSpannable.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.greenish_teal)), 0,
                todayText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        mDateTime.setText(timeSpannable);
    } else {
        mDateTime.setText(timeString);
    }
}

However, the color won't change.

enter image description here

Here is the XML for this view

<TextView
        android:id="@+id/newsfeed_date_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="23dp"
        android:textSize="12sp"
        android:textColor="@color/white_three"
        android:letterSpacing="0.06"
        app:fontPath="@string/opensans_bold_path"
        tools:text="Monday, January 1st"
        android:textAllCaps="true"
        tools:ignore="MissingPrefix"
        tools:targetApi="lollipop"/>

Solution

  • On versions prior to Oreo, the android:textAllCaps="true" attribute setting will cause the formatting spans to be stripped from your text. You'll need to remove that setting (or set it to false), and handle the conversion to upper case yourself, before creating your SpannableString from it. For example:

    String todayText = getResources().getString(R.string.today_marker);
    String text = todayText + timeString;
    
    Spannable timeSpannable = new SpannableString(text.toUpperCase());
    

    This is due to a known bug in the platform AllCapsTransformationMethod class, which on versions Nougat 7.1 and below handles the text as a flat String, basically stripping any formatting spans you may have set.

    Unfortunately, the support/androidx libraries also use the platform AllCapsTransformationMethod class, so this will happen for their textAllCaps attributes, as well; i.e., app:textAllCaps is broken pre-Oreo, too.

    As indicated, this was corrected in Oreo, so this manual fix isn't strictly necessary on those newer versions. However, if you are still supporting pre-Oreo versions, it might be easier to just leave it off and handle the capitalization manually everywhere, rather than having to account for two different setups in your resources and code.