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 if (view != null) { 184 view.setAlpha(appearing ? 0f : 1.0f); 185 view.setTranslationY(appearing ? translationY : 0); 186 Animator alphaAnim; 187 float targetAlpha = appearing ? 1f : 0f; 188 if (view.isHardwareAccelerated()) { 189 RenderNodeAnimator alphaAnimRt = new RenderNodeAnimator(RenderNodeAnimator.ALPHA, 190 targetAlpha); 191 alphaAnimRt.setTarget(view); 192 alphaAnim = alphaAnimRt; 193 } else { 194 alphaAnim = ObjectAnimator.ofFloat(view, View.ALPHA, view.getAlpha(), targetAlpha); 195 } 196 alphaAnim.setInterpolator(interpolator); 197 alphaAnim.setDuration(duration); 198 alphaAnim.setStartDelay(delay); 199 if (view.hasOverlappingRendering()) { 200 view.setLayerType(View.LAYER_TYPE_HARDWARE, null); 201 alphaAnim.addListener(new AnimatorListenerAdapter() { 202 @Override 203 public void onAnimationEnd(Animator animation) { 204 view.setLayerType(View.LAYER_TYPE_NONE, null); 205 } 206 }); 207 } 208 if (endRunnable != null) { 209 alphaAnim.addListener(new AnimatorListenerAdapter() { 210 @Override 211 public void onAnimationEnd(Animator animation) { 212 endRunnable.run(); 213 } 214 }); 215 } 216 alphaAnim.start(); 217 startTranslationYAnimation(view, delay, duration, appearing ? 0 : translationY, 218 interpolator); 219 } 220 } 221 222 /** 223 * A static method to start translation y animation 224 */ startTranslationYAnimation(View view, long delay, long duration, float endTranslationY, Interpolator interpolator)225 public static void startTranslationYAnimation(View view, long delay, long duration, 226 float endTranslationY, Interpolator interpolator) { 227 startTranslationYAnimation(view, delay, duration, endTranslationY, interpolator, null); 228 } 229 230 /** 231 * A static method to start translation y animation 232 */ startTranslationYAnimation(View view, long delay, long duration, float endTranslationY, Interpolator interpolator, Animator.AnimatorListener listener)233 public static void startTranslationYAnimation(View view, long delay, long duration, 234 float endTranslationY, Interpolator interpolator, Animator.AnimatorListener listener) { 235 Animator translationAnim; 236 if (view.isHardwareAccelerated()) { 237 RenderNodeAnimator translationAnimRt = new RenderNodeAnimator( 238 RenderNodeAnimator.TRANSLATION_Y, endTranslationY); 239 translationAnimRt.setTarget(view); 240 translationAnim = translationAnimRt; 241 } else { 242 translationAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, 243 view.getTranslationY(), endTranslationY); 244 } 245 translationAnim.setInterpolator(interpolator); 246 translationAnim.setDuration(duration); 247 translationAnim.setStartDelay(delay); 248 if (listener != null) { 249 translationAnim.addListener(listener); 250 } 251 translationAnim.start(); 252 } 253 254 public class AppearAnimationProperties { 255 public long[][] delays; 256 public int maxDelayRowIndex; 257 public int maxDelayColIndex; 258 } 259 260 public interface RowTranslationScaler { getRowTranslationScale(int row, int numRows)261 float getRowTranslationScale(int row, int numRows); 262 } 263 } 264