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 startTranslationYAnimation(View view, long delay, long duration, float endTranslationY, Interpolator interpolator)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 { getRowTranslationScale(int row, int numRows)247 float getRowTranslationScale(int row, int numRows); 248 } 249 } 250