KEMBAR78
Expand/Collapse animation on Android | PDF
squeezing precious milliseconds
Native Android animations
Animating values
val animator = ObjectAnimator.ofInt(0, 100)
Animating values
val animator = ObjectAnimator.ofInt(0, 100)
animator.duration = 1000
Animating values
val animator = ObjectAnimator.ofInt(0, 100)
animator.duration = 1000
animator.interpolator = FastOutSlowInInterpolator()
Animating values
val animator = ObjectAnimator.ofInt(0, 100)
animator.duration = 1000
animator.interpolator = FastOutSlowInInterpolator()
animator.addUpdateListener { Log.d("pasha", "${it.animatedValue}") }1
Animating values
val animator = ObjectAnimator.ofInt(0, 100)
animator.duration = 1000
animator.interpolator = FastOutSlowInInterpolator()
animator.addUpdateListener { Log.d("pasha", "${it.animatedValue}") }1
animator.start()
Animating values
val animator = ObjectAnimator.ofInt(0, 100)
animator.apply {
duration = 1000
interpolator = FastOutSlowInInterpolator()
addUpdateListener { Log.d("pasha", "${it.animatedValue}") }1
start()
}2
Animating values
Animating view properties
Animating view properties
Animating view properties
val animator = ObjectAnimator.ofFloat(1f, 1.5f)
animator.apply {
duration = 1000
interpolator = OvershootInterpolator()
addUpdateListener {
view.scaleX = it.animatedValue
view.scaleY = it.animatedValue
}1
start()
}2
Animating view properties
val animator = ObjectAnimator.ofFloat(1f, 1.5f)
animator.apply {
duration = 1000
interpolator = OvershootInterpolator()
addUpdateListener {
view.scaleX = it.animatedValue
view.scaleY = it.animatedValue
}1
start()
}2
Animating view properties
val animator = ObjectAnimator.ofFloat(1f, 1.5f)
animator.apply {
duration = 1000
interpolator = OvershootInterpolator()
addUpdateListener {
view.scaleX = it.animatedValue
view.scaleY = it.animatedValue
}1
start()
}2
Animating view properties
val animator = ObjectAnimator.ofFloat(1f, 1.5f)
animator.apply {
duration = 1000
interpolator = OvershootInterpolator()
addUpdateListener {
view.scaleX = it.animatedValue
view.scaleY = it.animatedValue
}1
start()
}2
Animating view properties
view.animate()
.scaleX(1.5f)
.scaleY(1.5f)
.setDuration(1000)
.setInterpolator(OvershootInterpolator())
.start()
Animating view properties
view.animate()
.scaleX(1.5f)
.scaleY(1.5f)
.setDuration(1000)
.setInterpolator(OvershootInterpolator())
.start()
Animating view properties
view.animate()
.scaleX(1.5f)
.scaleY(1.5f)
.setDuration(1000)
.setInterpolator(OvershootInterpolator())
.start()
Animating view properties
• scale
view.animate()
.scaleX(1.5f)
.scaleY(1.5f)
.setDuration(1000)
.setInterpolator(OvershootInterpolator())
.start()
Animating view properties
• scale
• alpha
view.animate()
.scaleX(1.5f)
.scaleY(1.5f)
.setDuration(1000)
.setInterpolator(OvershootInterpolator())
.start()
Animating view properties
• scale
• alpha
• translation
view.animate()
.scaleX(1.5f)
.scaleY(1.5f)
.setDuration(1000)
.setInterpolator(OvershootInterpolator())
.start()
Animating view properties
• scale
• alpha
• translation
• rotation
Changing view properties does
not trigger measure/layout pass
Animate LayoutParams!
private fun expand(view: View) {
}3
Animate LayoutParams!
private fun expand(view: View) {
val oldHeight = view.measuredHeight
val newHeight = oldHeight + 300
}3
Animate LayoutParams!
private fun expand(view: View) {
val oldHeight = view.measuredHeight
val newHeight = oldHeight + 300
ObjectAnimator.ofInt(oldHeight, newHeight).apply {
}2
}3
Animate LayoutParams!
private fun expand(view: View) {
val oldHeight = view.measuredHeight
val newHeight = oldHeight + 300
ObjectAnimator.ofInt(oldHeight, newHeight).apply {
duration = 1000
interpolator = FastOutSlowInInterpolator()
}2
}3
Animate LayoutParams!
private fun expand(view: View) {
val oldHeight = view.measuredHeight
val newHeight = oldHeight + 300
ObjectAnimator.ofInt(oldHeight, newHeight).apply {
duration = 1000
interpolator = FastOutSlowInInterpolator()
addUpdateListener {
view.layoutParams.height = it.animatedValue
}1
start()
}2
}3
Animate LayoutParams!
private fun expand(view: View) {
val oldHeight = view.measuredHeight
val newHeight = oldHeight + 300
ObjectAnimator.ofInt(oldHeight, newHeight).apply {
duration = 1000
interpolator = FastOutSlowInInterpolator()
addUpdateListener {
view.layoutParams.height = it.animatedValue
view.requestLayout()
}1
start()
}2
}3
Animate LayoutParams!
private fun expand(view: View) {
val oldHeight = view.measuredHeight
val newHeight = oldHeight + 300
ObjectAnimator.ofInt(oldHeight, newHeight).apply {
duration = 1000
interpolator = FastOutSlowInInterpolator()
addUpdateListener {
view.layoutParams.height = it.animatedValue
view.requestLayout()
}1
start()
}2
}3
Animate LayoutParams!
{16
ms
{16
ms
frames
Avoiding layouts
val oldHeight = textView.measuredHeight
Expand
val oldHeight = textView.measuredHeight
showFullText() // <-- request layout
Expand
val oldHeight = textView.measuredHeight
showFullText() // <-- request layout
Expand
val oldHeight = textView.measuredHeight
showFullText() // <-- request layout
textView.viewTreeObserver.addOnPreDrawListener {
}2
Expand
val oldHeight = textView.measuredHeight
showFullText() // <-- request layout
textView.viewTreeObserver.addOnPreDrawListener {
val sizeChange = textView.measuredHeight - oldHeight
}2
Expand
val oldHeight = textView.measuredHeight
showFullText() // <-- request layout
textView.viewTreeObserver.addOnPreDrawListener {
val sizeChange = textView.measuredHeight - oldHeight
}2
Expand
val oldHeight = textView.measuredHeight
showFullText() // <-- request layout
textView.viewTreeObserver.addOnPreDrawListener {
val sizeChange = textView.measuredHeight - oldHeight
footer.translationY -= sizeChange
}2
Expand
val oldHeight = textView.measuredHeight
showFullText() // <-- request layout
textView.viewTreeObserver.addOnPreDrawListener {
val sizeChange = textView.measuredHeight - oldHeight
footer.translationY -= sizeChange
}2
Expand
val oldHeight = textView.measuredHeight
showFullText() // <-- request layout
textView.viewTreeObserver.addOnPreDrawListener {
val sizeChange = textView.measuredHeight - oldHeight
footer.translationY -= sizeChange
val clipRect = Rect()
clipRect.right = root.measuredWidth
clipRect.bottom = root.measuredHeight - sizeChange
root.clipBounds = clipRect
}2
Expand
val oldHeight = textView.measuredHeight
showFullText() // <-- request layout
textView.viewTreeObserver.addOnPreDrawListener {
val sizeChange = textView.measuredHeight - oldHeight
footer.translationY -= sizeChange
val clipRect = Rect()
clipRect.right = root.measuredWidth
clipRect.bottom = root.measuredHeight - sizeChange
root.clipBounds = clipRect
}2
Expand
val oldHeight = textView.measuredHeight
showFullText() // <-- request layout
textView.viewTreeObserver.addOnPreDrawListener {
val sizeChange = textView.measuredHeight - oldHeight
footer.translationY -= sizeChange
val clipRect = Rect()
clipRect.right = root.measuredWidth
clipRect.bottom = root.measuredHeight - sizeChange
root.clipBounds = clipRect
ObjectAnimator
.ofFloat(-sizeChange, 0f)
.addUpdateListener {
footer.translationY = it.animatedValue
clipRect.bottom = root.measuredHeight + it.animatedValue
root.clipBounds = clipRect
}1
.start()
}2
Expand
val oldHeight = textView.measuredHeight
showFullText() // <-- request layout
textView.viewTreeObserver.addOnPreDrawListener {
val sizeChange = textView.measuredHeight - oldHeight
footer.translationY -= sizeChange
val clipRect = Rect()
clipRect.right = root.measuredWidth
clipRect.bottom = root.measuredHeight - sizeChange
root.clipBounds = clipRect
ObjectAnimator
.ofFloat(-sizeChange, 0f)
.addUpdateListener {
footer.translationY = it.animatedValue
clipRect.bottom = root.measuredHeight + it.animatedValue
root.clipBounds = clipRect
}1
.start()
}2
Expand
val oldHeight = textView.measuredHeight
showFullText() // <-- request layout
textView.viewTreeObserver.addOnPreDrawListener {
val sizeChange = textView.measuredHeight - oldHeight
footer.translationY -= sizeChange
val clipRect = Rect()
clipRect.right = root.measuredWidth
clipRect.bottom = root.measuredHeight - sizeChange
root.clipBounds = clipRect
ObjectAnimator
.ofFloat(-sizeChange, 0f)
.addUpdateListener {
footer.translationY = it.animatedValue
clipRect.bottom = root.measuredHeight + it.animatedValue
root.clipBounds = clipRect
}1
.start()
}2
Expand
val sizeChange = textView.measuredHeight - collapsedHeight
Collapse
val sizeChange = textView.measuredHeight - collapsedHeight
val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight)
Collapse
val sizeChange = textView.measuredHeight - collapsedHeight
val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight)
ObjectAnimator.ofInt(0, sizeChange).apply {
}3
Collapse
val sizeChange = textView.measuredHeight - collapsedHeight
val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight)
ObjectAnimator.ofInt(0, sizeChange).apply {
duration = 1000
interpolator = FastOutSlowInInterpolator()
}3
Collapse
val sizeChange = textView.measuredHeight - collapsedHeight
val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight)
ObjectAnimator.ofInt(0, sizeChange).apply {
duration = 1000
interpolator = FastOutSlowInInterpolator()
addUpdateListener {
clipRect.bottom = root.measuredHeight - animatedValue
footer.translationY -= animatedValue
}1
}3
Collapse
val sizeChange = textView.measuredHeight - collapsedHeight
val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight)
ObjectAnimator.ofInt(0, sizeChange).apply {
duration = 1000
interpolator = FastOutSlowInInterpolator()
addUpdateListener {
clipRect.bottom = root.measuredHeight - animatedValue
footer.translationY -= animatedValue
}1
}3
Collapse
val sizeChange = textView.measuredHeight - collapsedHeight
val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight)
ObjectAnimator.ofInt(0, sizeChange).apply {
duration = 1000
interpolator = FastOutSlowInInterpolator()
addUpdateListener {
clipRect.bottom = root.measuredHeight - animatedValue
footer.translationY -= animatedValue
}1
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
showShortText() // <-- request layout
}2
})
}3
Collapse
val sizeChange = textView.measuredHeight - collapsedHeight
val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight)
ObjectAnimator.ofInt(0, sizeChange).apply {
duration = 1000
interpolator = FastOutSlowInInterpolator()
addUpdateListener {
clipRect.bottom = root.measuredHeight - animatedValue
footer.translationY -= animatedValue
}1
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
showShortText() // <-- request layout
}2
})
}3
Collapse
Things to watch out for
Things to watch out for
• background drawables
Things to watch out for
• background drawables
• animate drawable bounds
Things to watch out for
• background drawables
• animate drawable bounds
• animating complex content
Things to watch out for
• background drawables
• animate drawable bounds
• animating complex content
• animating within scrollable container
Things to watch out for
• background drawables
• animate drawable bounds
• animating complex content
• animating within scrollable container
Things to watch out for
• background drawables
• animate drawable bounds
• animating complex content
• animating within scrollable container
Things to watch out for
Animating LayoutParams is
*not* bad
Animating LayoutParams is *not* bad
• know exactly which parts of layout are affected by your animation
Animating LayoutParams is *not* bad
• know exactly which parts of layout are affected by your animation
• measure performance with "Debug GPU Overdraw"
Animating LayoutParams is *not* bad
• know exactly which parts of layout are affected by your animation
• measure performance with "Debug GPU Overdraw"
• there is more than 1 way to achieve what you need
Animating LayoutParams is *not* bad
Thank you!
Pasha Dudka
@paveldudka
www.trickyandroid.com

Expand/Collapse animation on Android

  • 1.
  • 4.
  • 5.
    val animator =ObjectAnimator.ofInt(0, 100) Animating values
  • 6.
    val animator =ObjectAnimator.ofInt(0, 100) animator.duration = 1000 Animating values
  • 7.
    val animator =ObjectAnimator.ofInt(0, 100) animator.duration = 1000 animator.interpolator = FastOutSlowInInterpolator() Animating values
  • 8.
    val animator =ObjectAnimator.ofInt(0, 100) animator.duration = 1000 animator.interpolator = FastOutSlowInInterpolator() animator.addUpdateListener { Log.d("pasha", "${it.animatedValue}") }1 Animating values
  • 9.
    val animator =ObjectAnimator.ofInt(0, 100) animator.duration = 1000 animator.interpolator = FastOutSlowInInterpolator() animator.addUpdateListener { Log.d("pasha", "${it.animatedValue}") }1 animator.start() Animating values
  • 10.
    val animator =ObjectAnimator.ofInt(0, 100) animator.apply { duration = 1000 interpolator = FastOutSlowInInterpolator() addUpdateListener { Log.d("pasha", "${it.animatedValue}") }1 start() }2 Animating values
  • 11.
  • 12.
  • 13.
  • 14.
    val animator =ObjectAnimator.ofFloat(1f, 1.5f) animator.apply { duration = 1000 interpolator = OvershootInterpolator() addUpdateListener { view.scaleX = it.animatedValue view.scaleY = it.animatedValue }1 start() }2 Animating view properties
  • 15.
    val animator =ObjectAnimator.ofFloat(1f, 1.5f) animator.apply { duration = 1000 interpolator = OvershootInterpolator() addUpdateListener { view.scaleX = it.animatedValue view.scaleY = it.animatedValue }1 start() }2 Animating view properties
  • 16.
    val animator =ObjectAnimator.ofFloat(1f, 1.5f) animator.apply { duration = 1000 interpolator = OvershootInterpolator() addUpdateListener { view.scaleX = it.animatedValue view.scaleY = it.animatedValue }1 start() }2 Animating view properties
  • 17.
    val animator =ObjectAnimator.ofFloat(1f, 1.5f) animator.apply { duration = 1000 interpolator = OvershootInterpolator() addUpdateListener { view.scaleX = it.animatedValue view.scaleY = it.animatedValue }1 start() }2 Animating view properties
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
    Changing view propertiesdoes not trigger measure/layout pass
  • 28.
  • 29.
    private fun expand(view:View) { }3 Animate LayoutParams!
  • 30.
    private fun expand(view:View) { val oldHeight = view.measuredHeight val newHeight = oldHeight + 300 }3 Animate LayoutParams!
  • 31.
    private fun expand(view:View) { val oldHeight = view.measuredHeight val newHeight = oldHeight + 300 ObjectAnimator.ofInt(oldHeight, newHeight).apply { }2 }3 Animate LayoutParams!
  • 32.
    private fun expand(view:View) { val oldHeight = view.measuredHeight val newHeight = oldHeight + 300 ObjectAnimator.ofInt(oldHeight, newHeight).apply { duration = 1000 interpolator = FastOutSlowInInterpolator() }2 }3 Animate LayoutParams!
  • 33.
    private fun expand(view:View) { val oldHeight = view.measuredHeight val newHeight = oldHeight + 300 ObjectAnimator.ofInt(oldHeight, newHeight).apply { duration = 1000 interpolator = FastOutSlowInInterpolator() addUpdateListener { view.layoutParams.height = it.animatedValue }1 start() }2 }3 Animate LayoutParams!
  • 34.
    private fun expand(view:View) { val oldHeight = view.measuredHeight val newHeight = oldHeight + 300 ObjectAnimator.ofInt(oldHeight, newHeight).apply { duration = 1000 interpolator = FastOutSlowInInterpolator() addUpdateListener { view.layoutParams.height = it.animatedValue view.requestLayout() }1 start() }2 }3 Animate LayoutParams!
  • 35.
    private fun expand(view:View) { val oldHeight = view.measuredHeight val newHeight = oldHeight + 300 ObjectAnimator.ofInt(oldHeight, newHeight).apply { duration = 1000 interpolator = FastOutSlowInInterpolator() addUpdateListener { view.layoutParams.height = it.animatedValue view.requestLayout() }1 start() }2 }3 Animate LayoutParams!
  • 41.
  • 42.
  • 44.
  • 45.
    val oldHeight =textView.measuredHeight Expand
  • 46.
    val oldHeight =textView.measuredHeight showFullText() // <-- request layout Expand
  • 47.
    val oldHeight =textView.measuredHeight showFullText() // <-- request layout Expand
  • 48.
    val oldHeight =textView.measuredHeight showFullText() // <-- request layout textView.viewTreeObserver.addOnPreDrawListener { }2 Expand
  • 49.
    val oldHeight =textView.measuredHeight showFullText() // <-- request layout textView.viewTreeObserver.addOnPreDrawListener { val sizeChange = textView.measuredHeight - oldHeight }2 Expand
  • 50.
    val oldHeight =textView.measuredHeight showFullText() // <-- request layout textView.viewTreeObserver.addOnPreDrawListener { val sizeChange = textView.measuredHeight - oldHeight }2 Expand
  • 51.
    val oldHeight =textView.measuredHeight showFullText() // <-- request layout textView.viewTreeObserver.addOnPreDrawListener { val sizeChange = textView.measuredHeight - oldHeight footer.translationY -= sizeChange }2 Expand
  • 52.
    val oldHeight =textView.measuredHeight showFullText() // <-- request layout textView.viewTreeObserver.addOnPreDrawListener { val sizeChange = textView.measuredHeight - oldHeight footer.translationY -= sizeChange }2 Expand
  • 53.
    val oldHeight =textView.measuredHeight showFullText() // <-- request layout textView.viewTreeObserver.addOnPreDrawListener { val sizeChange = textView.measuredHeight - oldHeight footer.translationY -= sizeChange val clipRect = Rect() clipRect.right = root.measuredWidth clipRect.bottom = root.measuredHeight - sizeChange root.clipBounds = clipRect }2 Expand
  • 54.
    val oldHeight =textView.measuredHeight showFullText() // <-- request layout textView.viewTreeObserver.addOnPreDrawListener { val sizeChange = textView.measuredHeight - oldHeight footer.translationY -= sizeChange val clipRect = Rect() clipRect.right = root.measuredWidth clipRect.bottom = root.measuredHeight - sizeChange root.clipBounds = clipRect }2 Expand
  • 55.
    val oldHeight =textView.measuredHeight showFullText() // <-- request layout textView.viewTreeObserver.addOnPreDrawListener { val sizeChange = textView.measuredHeight - oldHeight footer.translationY -= sizeChange val clipRect = Rect() clipRect.right = root.measuredWidth clipRect.bottom = root.measuredHeight - sizeChange root.clipBounds = clipRect ObjectAnimator .ofFloat(-sizeChange, 0f) .addUpdateListener { footer.translationY = it.animatedValue clipRect.bottom = root.measuredHeight + it.animatedValue root.clipBounds = clipRect }1 .start() }2 Expand
  • 56.
    val oldHeight =textView.measuredHeight showFullText() // <-- request layout textView.viewTreeObserver.addOnPreDrawListener { val sizeChange = textView.measuredHeight - oldHeight footer.translationY -= sizeChange val clipRect = Rect() clipRect.right = root.measuredWidth clipRect.bottom = root.measuredHeight - sizeChange root.clipBounds = clipRect ObjectAnimator .ofFloat(-sizeChange, 0f) .addUpdateListener { footer.translationY = it.animatedValue clipRect.bottom = root.measuredHeight + it.animatedValue root.clipBounds = clipRect }1 .start() }2 Expand
  • 57.
    val oldHeight =textView.measuredHeight showFullText() // <-- request layout textView.viewTreeObserver.addOnPreDrawListener { val sizeChange = textView.measuredHeight - oldHeight footer.translationY -= sizeChange val clipRect = Rect() clipRect.right = root.measuredWidth clipRect.bottom = root.measuredHeight - sizeChange root.clipBounds = clipRect ObjectAnimator .ofFloat(-sizeChange, 0f) .addUpdateListener { footer.translationY = it.animatedValue clipRect.bottom = root.measuredHeight + it.animatedValue root.clipBounds = clipRect }1 .start() }2 Expand
  • 60.
    val sizeChange =textView.measuredHeight - collapsedHeight Collapse
  • 61.
    val sizeChange =textView.measuredHeight - collapsedHeight val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight) Collapse
  • 62.
    val sizeChange =textView.measuredHeight - collapsedHeight val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight) ObjectAnimator.ofInt(0, sizeChange).apply { }3 Collapse
  • 63.
    val sizeChange =textView.measuredHeight - collapsedHeight val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight) ObjectAnimator.ofInt(0, sizeChange).apply { duration = 1000 interpolator = FastOutSlowInInterpolator() }3 Collapse
  • 64.
    val sizeChange =textView.measuredHeight - collapsedHeight val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight) ObjectAnimator.ofInt(0, sizeChange).apply { duration = 1000 interpolator = FastOutSlowInInterpolator() addUpdateListener { clipRect.bottom = root.measuredHeight - animatedValue footer.translationY -= animatedValue }1 }3 Collapse
  • 65.
    val sizeChange =textView.measuredHeight - collapsedHeight val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight) ObjectAnimator.ofInt(0, sizeChange).apply { duration = 1000 interpolator = FastOutSlowInInterpolator() addUpdateListener { clipRect.bottom = root.measuredHeight - animatedValue footer.translationY -= animatedValue }1 }3 Collapse
  • 66.
    val sizeChange =textView.measuredHeight - collapsedHeight val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight) ObjectAnimator.ofInt(0, sizeChange).apply { duration = 1000 interpolator = FastOutSlowInInterpolator() addUpdateListener { clipRect.bottom = root.measuredHeight - animatedValue footer.translationY -= animatedValue }1 addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { showShortText() // <-- request layout }2 }) }3 Collapse
  • 67.
    val sizeChange =textView.measuredHeight - collapsedHeight val clipRect = Rect(0, 0, root.measuredWidth, root.measuredHeight) ObjectAnimator.ofInt(0, sizeChange).apply { duration = 1000 interpolator = FastOutSlowInInterpolator() addUpdateListener { clipRect.bottom = root.measuredHeight - animatedValue footer.translationY -= animatedValue }1 addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { showShortText() // <-- request layout }2 }) }3 Collapse
  • 70.
  • 71.
  • 72.
  • 73.
    • background drawables •animate drawable bounds Things to watch out for
  • 74.
    • background drawables •animate drawable bounds • animating complex content Things to watch out for
  • 75.
    • background drawables •animate drawable bounds • animating complex content • animating within scrollable container Things to watch out for
  • 76.
    • background drawables •animate drawable bounds • animating complex content • animating within scrollable container Things to watch out for
  • 77.
    • background drawables •animate drawable bounds • animating complex content • animating within scrollable container Things to watch out for
  • 78.
  • 79.
  • 80.
    • know exactlywhich parts of layout are affected by your animation Animating LayoutParams is *not* bad
  • 81.
    • know exactlywhich parts of layout are affected by your animation • measure performance with "Debug GPU Overdraw" Animating LayoutParams is *not* bad
  • 82.
    • know exactlywhich parts of layout are affected by your animation • measure performance with "Debug GPU Overdraw" • there is more than 1 way to achieve what you need Animating LayoutParams is *not* bad
  • 83.