1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.deskclock 18 19 import android.animation.Animator 20 import android.animation.AnimatorListenerAdapter 21 import android.animation.AnimatorSet 22 import android.animation.ObjectAnimator 23 import android.animation.PropertyValuesHolder 24 import android.view.View 25 import androidx.collection.ArrayMap 26 import androidx.recyclerview.widget.RecyclerView.State 27 import androidx.recyclerview.widget.RecyclerView.ViewHolder 28 import androidx.recyclerview.widget.RecyclerView.ItemAnimator 29 import androidx.recyclerview.widget.SimpleItemAnimator 30 31 class ItemAnimator : SimpleItemAnimator() { 32 private val mAddAnimatorsList: MutableList<Animator> = ArrayList() 33 private val mRemoveAnimatorsList: MutableList<Animator> = ArrayList() 34 private val mChangeAnimatorsList: MutableList<Animator> = ArrayList() 35 private val mMoveAnimatorsList: MutableList<Animator> = ArrayList() 36 37 private val mAnimators: MutableMap<ViewHolder, Animator> = ArrayMap() 38 animateRemovenull39 override fun animateRemove(holder: ViewHolder): Boolean { 40 endAnimation(holder) 41 42 val prevAlpha: Float = holder.itemView.getAlpha() 43 44 val removeAnimator: Animator? = ObjectAnimator.ofFloat(holder.itemView, View.ALPHA, 0f) 45 removeAnimator!!.duration = getRemoveDuration() 46 removeAnimator.addListener(object : AnimatorListenerAdapter() { 47 override fun onAnimationStart(animator: Animator) { 48 dispatchRemoveStarting(holder) 49 } 50 51 override fun onAnimationEnd(animator: Animator) { 52 animator.removeAllListeners() 53 mAnimators.remove(holder) 54 holder.itemView.setAlpha(prevAlpha) 55 dispatchRemoveFinished(holder) 56 } 57 }) 58 mRemoveAnimatorsList.add(removeAnimator) 59 mAnimators[holder] = removeAnimator 60 return true 61 } 62 animateAddnull63 override fun animateAdd(holder: ViewHolder): Boolean { 64 endAnimation(holder) 65 66 val prevAlpha: Float = holder.itemView.getAlpha() 67 holder.itemView.setAlpha(0f) 68 69 val addAnimator: Animator = ObjectAnimator.ofFloat(holder.itemView, View.ALPHA, 1f) 70 .setDuration(getAddDuration()) 71 addAnimator.addListener(object : AnimatorListenerAdapter() { 72 override fun onAnimationStart(animator: Animator) { 73 dispatchAddStarting(holder) 74 } 75 76 override fun onAnimationEnd(animator: Animator) { 77 animator.removeAllListeners() 78 mAnimators.remove(holder) 79 holder.itemView.setAlpha(prevAlpha) 80 dispatchAddFinished(holder) 81 } 82 }) 83 mAddAnimatorsList.add(addAnimator) 84 mAnimators[holder] = addAnimator 85 return true 86 } 87 animateMovenull88 override fun animateMove( 89 holder: ViewHolder, 90 fromX: Int, 91 fromY: Int, 92 toX: Int, 93 toY: Int 94 ): Boolean { 95 endAnimation(holder) 96 97 val deltaX = toX - fromX 98 val deltaY = toY - fromY 99 val moveDuration: Long = getMoveDuration() 100 101 if (deltaX == 0 && deltaY == 0) { 102 dispatchMoveFinished(holder) 103 return false 104 } 105 106 val view: View = holder.itemView 107 val prevTranslationX = view.translationX 108 val prevTranslationY = view.translationY 109 view.translationX = -deltaX.toFloat() 110 view.translationY = -deltaY.toFloat() 111 112 val moveAnimator: ObjectAnimator? 113 if (deltaX != 0 && deltaY != 0) { 114 val moveX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0f) 115 val moveY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0f) 116 moveAnimator = ObjectAnimator.ofPropertyValuesHolder(holder.itemView, moveX, moveY) 117 } else if (deltaX != 0) { 118 val moveX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0f) 119 moveAnimator = ObjectAnimator.ofPropertyValuesHolder(holder.itemView, moveX) 120 } else { 121 val moveY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0f) 122 moveAnimator = ObjectAnimator.ofPropertyValuesHolder(holder.itemView, moveY) 123 } 124 125 moveAnimator?.duration = moveDuration 126 moveAnimator.interpolator = AnimatorUtils.INTERPOLATOR_FAST_OUT_SLOW_IN 127 moveAnimator.addListener(object : AnimatorListenerAdapter() { 128 override fun onAnimationStart(animator: Animator?) { 129 dispatchMoveStarting(holder) 130 } 131 132 override fun onAnimationEnd(animator: Animator?) { 133 animator?.removeAllListeners() 134 mAnimators.remove(holder) 135 view.translationX = prevTranslationX 136 view.translationY = prevTranslationY 137 dispatchMoveFinished(holder) 138 } 139 }) 140 mMoveAnimatorsList.add(moveAnimator) 141 mAnimators[holder] = moveAnimator 142 143 return true 144 } 145 animateChangenull146 override fun animateChange( 147 oldHolder: ViewHolder, 148 newHolder: ViewHolder, 149 preInfo: ItemHolderInfo, 150 postInfo: ItemHolderInfo 151 ): Boolean { 152 endAnimation(oldHolder) 153 endAnimation(newHolder) 154 155 val changeDuration: Long = getChangeDuration() 156 val payloads = if (preInfo is PayloadItemHolderInfo) preInfo.payloads else null 157 158 if (oldHolder === newHolder) { 159 val animator = (newHolder as OnAnimateChangeListener) 160 .onAnimateChange(payloads, preInfo.left, preInfo.top, preInfo.right, 161 preInfo.bottom, changeDuration) 162 if (animator == null) { 163 dispatchChangeFinished(newHolder, false) 164 return false 165 } 166 animator.addListener(object : AnimatorListenerAdapter() { 167 override fun onAnimationStart(animator: Animator) { 168 dispatchChangeStarting(newHolder, false) 169 } 170 171 override fun onAnimationEnd(animator: Animator) { 172 animator.removeAllListeners() 173 mAnimators.remove(newHolder) 174 dispatchChangeFinished(newHolder, false) 175 } 176 }) 177 mChangeAnimatorsList.add(animator) 178 mAnimators[newHolder] = animator 179 return true 180 } else if (oldHolder !is OnAnimateChangeListener || 181 newHolder !is OnAnimateChangeListener) { 182 // Both holders must implement OnAnimateChangeListener in order to animate. 183 dispatchChangeFinished(oldHolder, true) 184 dispatchChangeFinished(newHolder, true) 185 return false 186 } 187 188 val oldChangeAnimator = (oldHolder as OnAnimateChangeListener) 189 .onAnimateChange(oldHolder, newHolder, changeDuration) 190 if (oldChangeAnimator != null) { 191 oldChangeAnimator.addListener(object : AnimatorListenerAdapter() { 192 override fun onAnimationStart(animator: Animator) { 193 dispatchChangeStarting(oldHolder, true) 194 } 195 196 override fun onAnimationEnd(animator: Animator) { 197 animator.removeAllListeners() 198 mAnimators.remove(oldHolder) 199 dispatchChangeFinished(oldHolder, true) 200 } 201 }) 202 mAnimators[oldHolder] = oldChangeAnimator 203 mChangeAnimatorsList.add(oldChangeAnimator) 204 } else { 205 dispatchChangeFinished(oldHolder, true) 206 } 207 208 val newChangeAnimator = (newHolder as OnAnimateChangeListener) 209 .onAnimateChange(oldHolder, newHolder, changeDuration) 210 if (newChangeAnimator != null) { 211 newChangeAnimator.addListener(object : AnimatorListenerAdapter() { 212 override fun onAnimationStart(animator: Animator) { 213 dispatchChangeStarting(newHolder, false) 214 } 215 216 override fun onAnimationEnd(animator: Animator) { 217 animator.removeAllListeners() 218 mAnimators.remove(newHolder) 219 dispatchChangeFinished(newHolder, false) 220 } 221 }) 222 mAnimators[newHolder] = newChangeAnimator 223 mChangeAnimatorsList.add(newChangeAnimator) 224 } else { 225 dispatchChangeFinished(newHolder, false) 226 } 227 228 return true 229 } 230 animateChangenull231 override fun animateChange( 232 oldHolder: ViewHolder, 233 newHolder: ViewHolder, 234 fromLeft: Int, 235 fromTop: Int, 236 toLeft: Int, 237 toTop: Int 238 ): Boolean { 239 /* Unused */ 240 throw IllegalStateException("This method should not be used") 241 } 242 runPendingAnimationsnull243 override fun runPendingAnimations() { 244 val removeAnimatorSet = AnimatorSet() 245 removeAnimatorSet.playTogether(mRemoveAnimatorsList) 246 mRemoveAnimatorsList.clear() 247 248 val addAnimatorSet = AnimatorSet() 249 addAnimatorSet.playTogether(mAddAnimatorsList) 250 mAddAnimatorsList.clear() 251 252 val changeAnimatorSet = AnimatorSet() 253 changeAnimatorSet.playTogether(mChangeAnimatorsList) 254 mChangeAnimatorsList.clear() 255 256 val moveAnimatorSet = AnimatorSet() 257 moveAnimatorSet.playTogether(mMoveAnimatorsList) 258 mMoveAnimatorsList.clear() 259 260 val pendingAnimatorSet = AnimatorSet() 261 pendingAnimatorSet.addListener(object : AnimatorListenerAdapter() { 262 override fun onAnimationEnd(animator: Animator) { 263 animator.removeAllListeners() 264 dispatchFinishedWhenDone() 265 } 266 }) 267 // Required order: removes, then changes & moves simultaneously, then additions. There are 268 // redundant edges because changes or moves may be empty, causing the removes to incorrectly 269 // play immediately. 270 pendingAnimatorSet.play(removeAnimatorSet).before(changeAnimatorSet) 271 pendingAnimatorSet.play(removeAnimatorSet).before(moveAnimatorSet) 272 pendingAnimatorSet.play(changeAnimatorSet).with(moveAnimatorSet) 273 pendingAnimatorSet.play(addAnimatorSet).after(changeAnimatorSet) 274 pendingAnimatorSet.play(addAnimatorSet).after(moveAnimatorSet) 275 pendingAnimatorSet.start() 276 } 277 endAnimationnull278 override fun endAnimation(holder: ViewHolder) { 279 val animator = mAnimators[holder] 280 281 mAnimators.remove(holder) 282 mAddAnimatorsList.remove(animator) 283 mRemoveAnimatorsList.remove(animator) 284 mChangeAnimatorsList.remove(animator) 285 mMoveAnimatorsList.remove(animator) 286 287 animator?.end() 288 dispatchFinishedWhenDone() 289 } 290 endAnimationsnull291 override fun endAnimations() { 292 val animatorList: MutableList<Animator?> = ArrayList(mAnimators.values) 293 for (animator in animatorList) { 294 animator?.end() 295 } 296 dispatchFinishedWhenDone() 297 } 298 isRunningnull299 override fun isRunning(): Boolean = mAnimators.isNotEmpty() 300 301 private fun dispatchFinishedWhenDone() { 302 if (!isRunning()) { 303 dispatchAnimationsFinished() 304 } 305 } 306 recordPreLayoutInformationnull307 override fun recordPreLayoutInformation( 308 state: State, 309 viewHolder: ViewHolder, 310 @AdapterChanges changeFlags: Int, 311 payloads: MutableList<Any> 312 ): ItemAnimator.ItemHolderInfo { 313 val itemHolderInfo: ItemHolderInfo = 314 super.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads) 315 if (itemHolderInfo is PayloadItemHolderInfo) { 316 itemHolderInfo.payloads = payloads 317 } 318 return itemHolderInfo 319 } 320 obtainHolderInfonull321 override fun obtainHolderInfo(): ItemAnimator.ItemHolderInfo { 322 return PayloadItemHolderInfo() 323 } 324 canReuseUpdatedViewHoldernull325 override fun canReuseUpdatedViewHolder( 326 viewHolder: ViewHolder, 327 payloads: MutableList<Any?> 328 ): Boolean { 329 val defaultReusePolicy: Boolean = super.canReuseUpdatedViewHolder(viewHolder, payloads) 330 // Whenever we have a payload, this is an in-place animation. 331 return payloads.isNotEmpty() || defaultReusePolicy 332 } 333 334 private class PayloadItemHolderInfo : ItemHolderInfo() { 335 private val mPayloads: MutableList<Any> = ArrayList() 336 337 var payloads: MutableList<Any> 338 get() = mPayloads 339 set(payloads) { 340 mPayloads.clear() 341 mPayloads.addAll(payloads) 342 } 343 } 344 345 interface OnAnimateChangeListener { onAnimateChangenull346 fun onAnimateChange(oldHolder: ViewHolder, newHolder: ViewHolder, duration: Long): Animator? 347 348 fun onAnimateChange( 349 payloads: List<Any>?, 350 fromLeft: Int, 351 fromTop: Int, 352 fromRight: Int, 353 fromBottom: Int, 354 duration: Long 355 ): Animator? 356 } 357 }