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.settingslib.animation; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ObjectAnimator; 22 import android.content.Context; 23 import android.view.RenderNodeAnimator; 24 import android.view.View; 25 import android.view.animation.AnimationUtils; 26 import android.view.animation.Interpolator; 27 28 import com.android.settingslib.R; 29 30 /** 31 * A class to make nice appear transitions for views in a tabular layout. 32 */ 33 public class AppearAnimationUtils implements AppearAnimationCreator<View> { 34 35 public static final long DEFAULT_APPEAR_DURATION = 220; 36 37 private final Interpolator mInterpolator; 38 private final float mStartTranslation; 39 private final AppearAnimationProperties mProperties = new AppearAnimationProperties(); 40 protected final float mDelayScale; 41 private final long mDuration; 42 protected RowTranslationScaler mRowTranslationScaler; 43 protected boolean mAppearing; 44 AppearAnimationUtils(Context ctx)45 public AppearAnimationUtils(Context ctx) { 46 this(ctx, DEFAULT_APPEAR_DURATION, 47 1.0f, 1.0f, 48 AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in)); 49 } 50 AppearAnimationUtils(Context ctx, long duration, float translationScaleFactor, float delayScaleFactor, Interpolator interpolator)51 public AppearAnimationUtils(Context ctx, long duration, float translationScaleFactor, 52 float delayScaleFactor, Interpolator interpolator) { 53 mInterpolator = interpolator; 54 mStartTranslation = ctx.getResources().getDimensionPixelOffset( 55 R.dimen.appear_y_translation_start) * translationScaleFactor; 56 mDelayScale = delayScaleFactor; 57 mDuration = duration; 58 mAppearing = true; 59 } 60 startAnimation2d(View[][] objects, final Runnable finishListener)61 public void startAnimation2d(View[][] objects, final Runnable finishListener) { 62 startAnimation2d(objects, finishListener, this); 63 } 64 startAnimation(View[] objects, final Runnable finishListener)65 public void startAnimation(View[] objects, final Runnable finishListener) { 66 startAnimation(objects, finishListener, this); 67 } 68 startAnimation2d(T[][] objects, final Runnable finishListener, AppearAnimationCreator<T> creator)69 public <T> void startAnimation2d(T[][] objects, final Runnable finishListener, 70 AppearAnimationCreator<T> creator) { 71 AppearAnimationProperties properties = getDelays(objects); 72 startAnimations(properties, objects, finishListener, creator); 73 } 74 startAnimation(T[] objects, final Runnable finishListener, AppearAnimationCreator<T> creator)75 public <T> void startAnimation(T[] objects, final Runnable finishListener, 76 AppearAnimationCreator<T> creator) { 77 AppearAnimationProperties properties = getDelays(objects); 78 startAnimations(properties, objects, finishListener, creator); 79 } 80 startAnimations(AppearAnimationProperties properties, T[] objects, final Runnable finishListener, AppearAnimationCreator<T> creator)81 private <T> void startAnimations(AppearAnimationProperties properties, T[] objects, 82 final Runnable finishListener, AppearAnimationCreator<T> creator) { 83 if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) { 84 finishListener.run(); 85 return; 86 } 87 for (int row = 0; row < properties.delays.length; row++) { 88 long[] columns = properties.delays[row]; 89 long delay = columns[0]; 90 Runnable endRunnable = null; 91 if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == 0) { 92 endRunnable = finishListener; 93 } 94 float translationScale = mRowTranslationScaler != null 95 ? mRowTranslationScaler.getRowTranslationScale(row, properties.delays.length) 96 : 1f; 97 float translation = translationScale * mStartTranslation; 98 creator.createAnimation(objects[row], delay, mDuration, 99 mAppearing ? translation : -translation, 100 mAppearing, mInterpolator, endRunnable); 101 } 102 } 103 startAnimations(AppearAnimationProperties properties, T[][] objects, final Runnable finishListener, AppearAnimationCreator<T> creator)104 private <T> void startAnimations(AppearAnimationProperties properties, T[][] objects, 105 final Runnable finishListener, AppearAnimationCreator<T> creator) { 106 if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) { 107 finishListener.run(); 108 return; 109 } 110 for (int row = 0; row < properties.delays.length; row++) { 111 long[] columns = properties.delays[row]; 112 float translationScale = mRowTranslationScaler != null 113 ? mRowTranslationScaler.getRowTranslationScale(row, properties.delays.length) 114 : 1f; 115 float translation = translationScale * mStartTranslation; 116 for (int col = 0; col < columns.length; col++) { 117 long delay = columns[col]; 118 Runnable endRunnable = null; 119 if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == col) { 120 endRunnable = finishListener; 121 } 122 creator.createAnimation(objects[row][col], delay, mDuration, 123 mAppearing ? translation : -translation, 124 mAppearing, mInterpolator, endRunnable); 125 } 126 } 127 } 128 getDelays(T[] items)129 private <T> AppearAnimationProperties getDelays(T[] items) { 130 long maxDelay = -1; 131 mProperties.maxDelayColIndex = -1; 132 mProperties.maxDelayRowIndex = -1; 133 mProperties.delays = new long[items.length][]; 134 for (int row = 0; row < items.length; row++) { 135 mProperties.delays[row] = new long[1]; 136 long delay = calculateDelay(row, 0); 137 mProperties.delays[row][0] = delay; 138 if (items[row] != null && delay > maxDelay) { 139 maxDelay = delay; 140 mProperties.maxDelayColIndex = 0; 141 mProperties.maxDelayRowIndex = row; 142 } 143 } 144 return mProperties; 145 } 146 getDelays(T[][] items)147 private <T> AppearAnimationProperties getDelays(T[][] items) { 148 long maxDelay = -1; 149 mProperties.maxDelayColIndex = -1; 150 mProperties.maxDelayRowIndex = -1; 151 mProperties.delays = new long[items.length][]; 152 for (int row = 0; row < items.length; row++) { 153 T[] columns = items[row]; 154 mProperties.delays[row] = new long[columns.length]; 155 for (int col = 0; col < columns.length; col++) { 156 long delay = calculateDelay(row, col); 157 mProperties.delays[row][col] = delay; 158 if (items[row][col] != null && delay > maxDelay) { 159 maxDelay = delay; 160 mProperties.maxDelayColIndex = col; 161 mProperties.maxDelayRowIndex = row; 162 } 163 } 164 } 165 return mProperties; 166 } 167 calculateDelay(int row, int col)168 protected long calculateDelay(int row, int col) { 169 return (long) ((row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20) * mDelayScale); 170 } 171 getInterpolator()172 public Interpolator getInterpolator() { 173 return mInterpolator; 174 } 175 getStartTranslation()176 public float getStartTranslation() { 177 return mStartTranslation; 178 } 179 180 @Override createAnimation(final View view, long delay, long duration, float translationY, boolean appearing, Interpolator interpolator, final Runnable endRunnable)181 public void createAnimation(final View view, long delay, long duration, float translationY, 182 boolean appearing, Interpolator interpolator, final Runnable endRunnable) { 183 createAnimation( 184 view, delay, duration, translationY, appearing, interpolator, endRunnable, null); 185 } 186 187 @Override createAnimation(final View view, long delay, long duration, float translationY, boolean appearing, Interpolator interpolator, final Runnable endRunnable, final AnimatorListenerAdapter animatorListener)188 public void createAnimation(final View view, long delay, 189 long duration, float translationY, boolean appearing, Interpolator interpolator, 190 final Runnable endRunnable, final AnimatorListenerAdapter animatorListener) { 191 if (view != null) { 192 float targetAlpha = appearing ? 1f : 0f; 193 float targetTranslationY = appearing ? 0 : translationY; 194 view.setAlpha(1.0f - targetAlpha); 195 view.setTranslationY(translationY - targetTranslationY); 196 Animator alphaAnim; 197 198 if (view.isHardwareAccelerated()) { 199 RenderNodeAnimator alphaAnimRt = new RenderNodeAnimator(RenderNodeAnimator.ALPHA, 200 targetAlpha); 201 alphaAnimRt.setTarget(view); 202 alphaAnim = alphaAnimRt; 203 } else { 204 alphaAnim = ObjectAnimator.ofFloat(view, View.ALPHA, view.getAlpha(), targetAlpha); 205 } 206 alphaAnim.setInterpolator(interpolator); 207 alphaAnim.setDuration(duration); 208 alphaAnim.setStartDelay(delay); 209 if (view.hasOverlappingRendering()) { 210 view.setLayerType(View.LAYER_TYPE_HARDWARE, null); 211 alphaAnim.addListener(new AnimatorListenerAdapter() { 212 @Override 213 public void onAnimationEnd(Animator animation) { 214 view.setLayerType(View.LAYER_TYPE_NONE, null); 215 } 216 }); 217 } 218 alphaAnim.addListener(new AnimatorListenerAdapter() { 219 @Override 220 public void onAnimationEnd(Animator animation) { 221 view.setAlpha(targetAlpha); 222 if (endRunnable != null) { 223 endRunnable.run(); 224 } 225 } 226 }); 227 alphaAnim.start(); 228 startTranslationYAnimation(view, delay, duration, targetTranslationY, 229 interpolator, animatorListener); 230 } 231 } 232 233 /** 234 * A static method to start translation y animation 235 */ startTranslationYAnimation(View view, long delay, long duration, float endTranslationY, Interpolator interpolator)236 public static void startTranslationYAnimation(View view, long delay, long duration, 237 float endTranslationY, Interpolator interpolator) { 238 startTranslationYAnimation(view, delay, duration, endTranslationY, interpolator, null); 239 } 240 241 /** 242 * A static method to start translation y animation 243 */ startTranslationYAnimation(View view, long delay, long duration, float endTranslationY, Interpolator interpolator, AnimatorListenerAdapter listener)244 public static void startTranslationYAnimation(View view, long delay, long duration, 245 float endTranslationY, Interpolator interpolator, AnimatorListenerAdapter listener) { 246 Animator translationAnim; 247 if (view.isHardwareAccelerated()) { 248 RenderNodeAnimator translationAnimRt = new RenderNodeAnimator( 249 RenderNodeAnimator.TRANSLATION_Y, endTranslationY); 250 translationAnimRt.setTarget(view); 251 translationAnim = translationAnimRt; 252 } else { 253 translationAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, 254 view.getTranslationY(), endTranslationY); 255 } 256 translationAnim.setInterpolator(interpolator); 257 translationAnim.setDuration(duration); 258 translationAnim.setStartDelay(delay); 259 if (listener != null) { 260 translationAnim.addListener(listener); 261 } 262 translationAnim.addListener(new AnimatorListenerAdapter() { 263 @Override 264 public void onAnimationEnd(Animator animation) { 265 view.setTranslationY(endTranslationY); 266 } 267 }); 268 translationAnim.start(); 269 } 270 271 public class AppearAnimationProperties { 272 public long[][] delays; 273 public int maxDelayRowIndex; 274 public int maxDelayColIndex; 275 } 276 277 public interface RowTranslationScaler { getRowTranslationScale(int row, int numRows)278 float getRowTranslationScale(int row, int numRows); 279 } 280 } 281