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 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 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 61 public void startAnimation2d(View[][] objects, final Runnable finishListener) { 62 startAnimation2d(objects, finishListener, this); 63 } 64 65 public void startAnimation(View[] objects, final Runnable finishListener) { 66 startAnimation(objects, finishListener, this); 67 } 68 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 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 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 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 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 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 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 172 public Interpolator getInterpolator() { 173 return mInterpolator; 174 } 175 176 public float getStartTranslation() { 177 return mStartTranslation; 178 } 179 180 @Override 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 public static void startTranslationYAnimation(View view, long delay, long duration, 223 float endTranslationY, Interpolator interpolator) { 224 Animator translationAnim; 225 if (view.isHardwareAccelerated()) { 226 RenderNodeAnimator translationAnimRt = new RenderNodeAnimator( 227 RenderNodeAnimator.TRANSLATION_Y, endTranslationY); 228 translationAnimRt.setTarget(view); 229 translationAnim = translationAnimRt; 230 } else { 231 translationAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, 232 view.getTranslationY(), endTranslationY); 233 } 234 translationAnim.setInterpolator(interpolator); 235 translationAnim.setDuration(duration); 236 translationAnim.setStartDelay(delay); 237 translationAnim.start(); 238 } 239 240 public class AppearAnimationProperties { 241 public long[][] delays; 242 public int maxDelayRowIndex; 243 public int maxDelayColIndex; 244 } 245 246 public interface RowTranslationScaler { 247 float getRowTranslationScale(int row, int numRows); 248 } 249 } 250