1

I have custom textAppearance defined in my themes. My Theme looks like this

<style name="Theme.MyApp" parent="Theme.MaterialComponents.Light">
.....
<item name="textAppearanceHeadline3">@style/TextAppearance.MySpark.Headline3</item>
.....
</style>

Here is the style TextAppearance.MyApp.Headline3

<style name="TextAppearance.MyApp.Headline3" parent="TextAppearance.MaterialComponents.Headline3">
        <item name="fontFamily">@font/avenir_next_demi</item>
        <item name="android:textSize">40sp</item>
        <item name="android:gravity">left|top</item>
        <item name="android:textStyle">bold</item>
        <item name="android:textColor">?attr/colorOnSurface</item>
    </style>

Works for XML

<com.google.android.material.textview.MaterialTextView
        android:textAppearance="?attr/textAppearanceHeadline3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/add_product_description"/>

WORKS for programmatically referencing style

textView.setTextAppearance(R.style.TextAppearance_MyApp_Headline3)

DOESN'T WORK FOR When I want to apply textAppearance programmatically using attr, I couldn't find a way yet for something like this.

textView.setTextAppearance(R.attr.textAppearanceHeadline3)

I would like to use attr which helps me in switching different themes.

Cœur
  • 37,241
  • 25
  • 195
  • 267
sha
  • 1,410
  • 2
  • 18
  • 37
  • I think you might be looking to [resolve theme attributes](https://stackoverflow.com/a/17277714/5288316). In this case the `TypedValue` data is the text appearance resource ID. – Nicolas Feb 04 '20 at 00:34
  • @Nicolas I'm using `TypedValue` approach for colours. I don't think you can use the same approach to textAppearance as it has many properties under it. – sha Feb 04 '20 at 00:47
  • There's another approach in the [second answer](https://stackoverflow.com/a/39377287/5288316) with `obtainStyledAttributes`. What you resolve is always the resource ID (an integer) referenced by the attribute, which is then used by the TextView to resolve more attributes. (see the [source](https://github.com/aosp-mirror/platform_frameworks_base/blob/602faa961aed934a4cd77913db7fc9e6ccf2e25b/core/java/android/widget/TextView.java#L3821)) – Nicolas Feb 04 '20 at 01:03
  • @Nicolas Thanks for that. With this approach, I have to read all the attributes inside `textAppearance` and apply to textview. This is pretty error-prone. If I add/delete any properties in style, I have to remember and change in my class as well. This seems like an easy miss. – sha Feb 04 '20 at 01:09
  • See my answer, I hope that clarifies things. – Nicolas Feb 04 '20 at 01:32

1 Answers1

5

Every Android resource has an integer ID associated to it. Attributes only reference the resource ID, not the resource itself. To resolve this resource ID from an attribute, you use Context.obtainStyledAttributes with an array of attributes:

val attrs = intArrayOf(R.attr.myTextAppearance)       // The array of attributes we're interested in.
val ta = context.obtainStyledAttributes(attrs)        // Get the value referenced by the attributes in the array
val resId = ta.getResourceId(0, 0)                    // The first 0 is the index in the 'attrs' array.
ta.recycle()                                          // Don't forget that! You can also use TypedArray.use { } extensions from android KTX.
TextViewCompat.setTextAppearance(textView, resId)     // Utility method to set text appearance for all SDK versions

A text appearance is just a regular style which has a higher priority when applied on the text view, and only text attributes are used. That's what I wanted to show when I linked the TextView source code.

When you pass a resource ID to setTextAppearance, the TextView resolves all the attributes values in the TextAppearance styleable (basically a list of attributes), storing the values in a TypedArray, then reads and applies them.

Using resolveAttribute like I first suggested is similar to using obtainStyledAttributes, but the TypedValue is set automatically to the attribute value, in this case a resource ID, without having to call getResourceId.

Nicolas
  • 6,611
  • 3
  • 29
  • 73
  • Wow. Such a good explanation. I was thinking text appearance is a bit different. Thanks for explaining. It worked – sha Feb 04 '20 at 21:12