1 /* 2 * Copyright (C) 2015 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.documentsui.dirlist; 18 19 import android.animation.Animator; 20 import android.animation.ArgbEvaluator; 21 import android.animation.ValueAnimator; 22 import android.content.Context; 23 import androidx.collection.ArrayMap; 24 import androidx.recyclerview.widget.DefaultItemAnimator; 25 import androidx.recyclerview.widget.RecyclerView; 26 27 import android.content.res.TypedArray; 28 import android.util.TypedValue; 29 30 import com.android.documentsui.R; 31 32 import java.util.ArrayList; 33 import java.util.List; 34 import java.util.Map; 35 36 /** 37 * Performs change animations on Items in DirectoryFragment's RecyclerView. This class overrides 38 * the way selection animations are normally performed - instead of cross fading the old Item with a 39 * new Item, this class manually animates a background color change. This enables selected Items to 40 * correctly maintain focus. 41 */ 42 class DirectoryItemAnimator extends DefaultItemAnimator { 43 private final List<ColorAnimation> mPendingAnimations = new ArrayList<>(); 44 private final Map<RecyclerView.ViewHolder, ColorAnimation> mRunningAnimations = 45 new ArrayMap<>(); 46 47 @Override runPendingAnimations()48 public void runPendingAnimations() { 49 super.runPendingAnimations(); 50 for (ColorAnimation anim: mPendingAnimations) { 51 anim.start(); 52 mRunningAnimations.put(anim.viewHolder, anim); 53 } 54 mPendingAnimations.clear(); 55 } 56 57 @Override endAnimation(RecyclerView.ViewHolder vh)58 public void endAnimation(RecyclerView.ViewHolder vh) { 59 super.endAnimation(vh); 60 61 for (int i = mPendingAnimations.size() - 1; i >= 0; --i) { 62 ColorAnimation anim = mPendingAnimations.get(i); 63 if (anim.viewHolder == vh) { 64 mPendingAnimations.remove(i); 65 anim.end(); 66 } 67 } 68 69 ColorAnimation anim = mRunningAnimations.get(vh); 70 if (anim != null) { 71 anim.cancel(); 72 } 73 } 74 75 @Override recordPreLayoutInformation( RecyclerView.State state, RecyclerView.ViewHolder viewHolder, @AdapterChanges int changeFlags, List<Object> payloads)76 public ItemHolderInfo recordPreLayoutInformation( 77 RecyclerView.State state, 78 RecyclerView.ViewHolder viewHolder, 79 @AdapterChanges int changeFlags, 80 List<Object> payloads) { 81 ItemInfo info = (ItemInfo) super.recordPreLayoutInformation(state, 82 viewHolder, changeFlags, payloads); 83 info.isActivated = viewHolder.itemView.isActivated(); 84 return info; 85 } 86 87 88 @Override recordPostLayoutInformation( RecyclerView.State state, RecyclerView.ViewHolder viewHolder)89 public ItemHolderInfo recordPostLayoutInformation( 90 RecyclerView.State state, RecyclerView.ViewHolder viewHolder) { 91 ItemInfo info = (ItemInfo) super.recordPostLayoutInformation(state, 92 viewHolder); 93 info.isActivated = viewHolder.itemView.isActivated(); 94 return info; 95 } 96 97 @Override obtainHolderInfo()98 public ItemHolderInfo obtainHolderInfo() { 99 return new ItemInfo(); 100 } 101 102 @Override canReuseUpdatedViewHolder(RecyclerView.ViewHolder vh)103 public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder vh) { 104 return true; 105 } 106 107 class ItemInfo extends DefaultItemAnimator.ItemHolderInfo { 108 boolean isActivated; 109 }; 110 111 /** 112 * Animates changes in background color. 113 */ 114 class ColorAnimation 115 implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener { 116 ValueAnimator mValueAnimator; 117 final RecyclerView.ViewHolder viewHolder; 118 int mEndColor; 119 ColorAnimation(RecyclerView.ViewHolder vh, int startColor, int endColor)120 public ColorAnimation(RecyclerView.ViewHolder vh, int startColor, int endColor) 121 { 122 viewHolder = vh; 123 mValueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), startColor, endColor); 124 mValueAnimator.addUpdateListener(this); 125 mValueAnimator.addListener(this); 126 127 mEndColor = endColor; 128 } 129 start()130 public void start() { 131 mValueAnimator.start(); 132 } 133 cancel()134 public void cancel() { 135 mValueAnimator.cancel(); 136 } 137 end()138 public void end() { 139 mValueAnimator.end(); 140 } 141 142 @Override onAnimationUpdate(ValueAnimator animator)143 public void onAnimationUpdate(ValueAnimator animator) { 144 viewHolder.itemView.setBackgroundColor((Integer)animator.getAnimatedValue()); 145 } 146 147 @Override onAnimationEnd(Animator animator)148 public void onAnimationEnd(Animator animator) { 149 viewHolder.itemView.setBackgroundColor(mEndColor); 150 mRunningAnimations.remove(viewHolder); 151 dispatchAnimationFinished(viewHolder); 152 } 153 154 @Override onAnimationStart(Animator animation)155 public void onAnimationStart(Animator animation) { 156 dispatchAnimationStarted(viewHolder); 157 } 158 159 @Override onAnimationCancel(Animator animation)160 public void onAnimationCancel(Animator animation) {} 161 162 @Override onAnimationRepeat(Animator animation)163 public void onAnimationRepeat(Animator animation) {} 164 }; 165 }; 166