Cache Typeface for later animation

The Paint#setFontvariationSettings always creates new Typeface instance
from scratch. To avoid unnecessary Typeface creation for the same weight
variant, cache the Typeface and use it later same animation.

Bug: 189867246
Test: atest TextAnimatorTest TextInterpolatorTest

Change-Id: I67ca72f2c54ca9caf4bcf562557ac4f24b44f01e
This commit is contained in:
Seigo Nonaka
2021-06-10 00:24:38 -07:00
parent 33f8e11606
commit 80d73fa4eb
2 changed files with 58 additions and 1 deletions

View File

@@ -21,7 +21,9 @@ import android.animation.AnimatorListenerAdapter
import android.animation.TimeInterpolator
import android.animation.ValueAnimator
import android.graphics.Canvas
import android.graphics.Typeface
import android.text.Layout
import android.util.SparseArray
private const val TAG_WGHT = "wght"
private const val DEFAULT_ANIMATION_DURATION: Long = 300
@@ -72,6 +74,8 @@ class TextAnimator(
})
}
private val typefaceCache = SparseArray<Typeface?>()
fun updateLayout(layout: Layout) {
textInterpolator.layout = layout
}
@@ -120,7 +124,12 @@ class TextAnimator(
textInterpolator.targetPaint.textSize = textSize
}
if (weight >= 0) {
textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
// Paint#setFontVariationSettings creates Typeface instance from scratch. To reduce the
// memory impact, cache the typeface result.
textInterpolator.targetPaint.typeface = typefaceCache.getOrElse(weight) {
textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
textInterpolator.targetPaint.typeface
}
}
if (color != null) {
textInterpolator.targetPaint.color = color
@@ -155,3 +164,12 @@ class TextAnimator(
}
}
}
private fun <V> SparseArray<V>.getOrElse(key: Int, defaultValue: () -> V): V {
var v = get(key)
if (v == null) {
v = defaultValue()
put(key, v)
}
return v
}

View File

@@ -18,12 +18,14 @@ package com.android.keyguard
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.graphics.Typeface
import android.testing.AndroidTestingRunner
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -140,4 +142,41 @@ class TextAnimatorTest : SysuiTestCase() {
verify(animationEndCallback).run()
verify(valueAnimator).removeListener(eq(captor.value))
}
@Test
fun testCacheTypeface() {
val layout = makeLayout("Hello, World", PAINT)
val valueAnimator = mock(ValueAnimator::class.java)
val textInterpolator = mock(TextInterpolator::class.java)
val paint = TextPaint().apply {
typeface = Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf")
}
`when`(textInterpolator.targetPaint).thenReturn(paint)
val textAnimator = TextAnimator(layout, {}).apply {
this.textInterpolator = textInterpolator
this.animator = valueAnimator
}
textAnimator.setTextStyle(
weight = 400,
animate = true
)
val prevTypeface = paint.typeface
textAnimator.setTextStyle(
weight = 700,
animate = true
)
assertThat(paint.typeface).isNotSameInstanceAs(prevTypeface)
textAnimator.setTextStyle(
weight = 400,
animate = true
)
assertThat(paint.typeface).isSameInstanceAs(prevTypeface)
}
}