Home | History | Annotate | Download | only in animation
      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