Home | History | Annotate | Download | only in transition
      1 /*
      2  * Copyright (C) 2014 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 package android.transition;
     17 
     18 import android.animation.Animator;
     19 import android.animation.TimeInterpolator;
     20 import android.annotation.IntDef;
     21 import android.content.Context;
     22 import android.content.res.TypedArray;
     23 import android.util.AttributeSet;
     24 import android.view.Gravity;
     25 import android.view.View;
     26 import android.view.ViewGroup;
     27 import android.view.animation.AccelerateInterpolator;
     28 import android.view.animation.DecelerateInterpolator;
     29 
     30 import com.android.internal.R;
     31 
     32 import java.lang.annotation.Retention;
     33 import java.lang.annotation.RetentionPolicy;
     34 
     35 /**
     36  * This transition tracks changes to the visibility of target views in the
     37  * start and end scenes and moves views in or out from one of the edges of the
     38  * scene. Visibility is determined by both the
     39  * {@link View#setVisibility(int)} state of the view as well as whether it
     40  * is parented in the current view hierarchy. Disappearing Views are
     41  * limited as described in {@link Visibility#onDisappear(android.view.ViewGroup,
     42  * TransitionValues, int, TransitionValues, int)}.
     43  */
     44 public class Slide extends Visibility {
     45     private static final String TAG = "Slide";
     46     private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
     47     private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
     48     private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition";
     49     private CalculateSlide mSlideCalculator = sCalculateBottom;
     50     private @GravityFlag int mSlideEdge = Gravity.BOTTOM;
     51     private float mSlideFraction = 1;
     52 
     53     /** @hide */
     54     @Retention(RetentionPolicy.SOURCE)
     55     @IntDef({Gravity.LEFT, Gravity.TOP, Gravity.RIGHT, Gravity.BOTTOM, Gravity.START, Gravity.END})
     56     public @interface GravityFlag {}
     57 
     58     private interface CalculateSlide {
     59 
     60         /** Returns the translation value for view when it goes out of the scene */
     61         float getGoneX(ViewGroup sceneRoot, View view, float fraction);
     62 
     63         /** Returns the translation value for view when it goes out of the scene */
     64         float getGoneY(ViewGroup sceneRoot, View view, float fraction);
     65     }
     66 
     67     private static abstract class CalculateSlideHorizontal implements CalculateSlide {
     68 
     69         @Override
     70         public float getGoneY(ViewGroup sceneRoot, View view, float fraction) {
     71             return view.getTranslationY();
     72         }
     73     }
     74 
     75     private static abstract class CalculateSlideVertical implements CalculateSlide {
     76 
     77         @Override
     78         public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
     79             return view.getTranslationX();
     80         }
     81     }
     82 
     83     private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() {
     84         @Override
     85         public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
     86             return view.getTranslationX() - sceneRoot.getWidth() * fraction;
     87         }
     88     };
     89 
     90     private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() {
     91         @Override
     92         public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
     93             final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
     94             final float x;
     95             if (isRtl) {
     96                 x = view.getTranslationX() + sceneRoot.getWidth() * fraction;
     97             } else {
     98                 x = view.getTranslationX() - sceneRoot.getWidth() * fraction;
     99             }
    100             return x;
    101         }
    102     };
    103 
    104     private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
    105         @Override
    106         public float getGoneY(ViewGroup sceneRoot, View view, float fraction) {
    107             return view.getTranslationY() - sceneRoot.getHeight() * fraction;
    108         }
    109     };
    110 
    111     private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() {
    112         @Override
    113         public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
    114             return view.getTranslationX() + sceneRoot.getWidth() * fraction;
    115         }
    116     };
    117 
    118     private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() {
    119         @Override
    120         public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
    121             final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
    122             final float x;
    123             if (isRtl) {
    124                 x = view.getTranslationX() - sceneRoot.getWidth() * fraction;
    125             } else {
    126                 x = view.getTranslationX() + sceneRoot.getWidth() * fraction;
    127             }
    128             return x;
    129         }
    130     };
    131 
    132     private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
    133         @Override
    134         public float getGoneY(ViewGroup sceneRoot, View view, float fraction) {
    135             return view.getTranslationY() + sceneRoot.getHeight() * fraction;
    136         }
    137     };
    138 
    139     /**
    140      * Constructor using the default {@link Gravity#BOTTOM}
    141      * slide edge direction.
    142      */
    143     public Slide() {
    144         setSlideEdge(Gravity.BOTTOM);
    145     }
    146 
    147     /**
    148      * Constructor using the provided slide edge direction.
    149      */
    150     public Slide(int slideEdge) {
    151         setSlideEdge(slideEdge);
    152     }
    153 
    154     public Slide(Context context, AttributeSet attrs) {
    155         super(context, attrs);
    156         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Slide);
    157         int edge = a.getInt(R.styleable.Slide_slideEdge, Gravity.BOTTOM);
    158         a.recycle();
    159         setSlideEdge(edge);
    160     }
    161 
    162     private void captureValues(TransitionValues transitionValues) {
    163         View view = transitionValues.view;
    164         int[] position = new int[2];
    165         view.getLocationOnScreen(position);
    166         transitionValues.values.put(PROPNAME_SCREEN_POSITION, position);
    167     }
    168 
    169     @Override
    170     public void captureStartValues(TransitionValues transitionValues) {
    171         super.captureStartValues(transitionValues);
    172         captureValues(transitionValues);
    173     }
    174 
    175     @Override
    176     public void captureEndValues(TransitionValues transitionValues) {
    177         super.captureEndValues(transitionValues);
    178         captureValues(transitionValues);
    179     }
    180 
    181     /**
    182      * Change the edge that Views appear and disappear from.
    183      *
    184      * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of
    185      *                  {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
    186      *                  {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM},
    187      *                  {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
    188      * @attr ref android.R.styleable#Slide_slideEdge
    189      */
    190     public void setSlideEdge(@GravityFlag int slideEdge) {
    191         switch (slideEdge) {
    192             case Gravity.LEFT:
    193                 mSlideCalculator = sCalculateLeft;
    194                 break;
    195             case Gravity.TOP:
    196                 mSlideCalculator = sCalculateTop;
    197                 break;
    198             case Gravity.RIGHT:
    199                 mSlideCalculator = sCalculateRight;
    200                 break;
    201             case Gravity.BOTTOM:
    202                 mSlideCalculator = sCalculateBottom;
    203                 break;
    204             case Gravity.START:
    205                 mSlideCalculator = sCalculateStart;
    206                 break;
    207             case Gravity.END:
    208                 mSlideCalculator = sCalculateEnd;
    209                 break;
    210             default:
    211                 throw new IllegalArgumentException("Invalid slide direction");
    212         }
    213         mSlideEdge = slideEdge;
    214         SidePropagation propagation = new SidePropagation();
    215         propagation.setSide(slideEdge);
    216         setPropagation(propagation);
    217     }
    218 
    219     /**
    220      * Returns the edge that Views appear and disappear from.
    221      *
    222      * @return the edge of the scene to use for Views appearing and disappearing. One of
    223      *         {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
    224      *         {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM},
    225      *         {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
    226      * @attr ref android.R.styleable#Slide_slideEdge
    227      */
    228     @GravityFlag
    229     public int getSlideEdge() {
    230         return mSlideEdge;
    231     }
    232 
    233     @Override
    234     public Animator onAppear(ViewGroup sceneRoot, View view,
    235             TransitionValues startValues, TransitionValues endValues) {
    236         if (endValues == null) {
    237             return null;
    238         }
    239         int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION);
    240         float endX = view.getTranslationX();
    241         float endY = view.getTranslationY();
    242         float startX = mSlideCalculator.getGoneX(sceneRoot, view, mSlideFraction);
    243         float startY = mSlideCalculator.getGoneY(sceneRoot, view, mSlideFraction);
    244         return TranslationAnimationCreator
    245                 .createAnimation(view, endValues, position[0], position[1],
    246                         startX, startY, endX, endY, sDecelerate, this);
    247     }
    248 
    249     @Override
    250     public Animator onDisappear(ViewGroup sceneRoot, View view,
    251             TransitionValues startValues, TransitionValues endValues) {
    252         if (startValues == null) {
    253             return null;
    254         }
    255         int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION);
    256         float startX = view.getTranslationX();
    257         float startY = view.getTranslationY();
    258         float endX = mSlideCalculator.getGoneX(sceneRoot, view, mSlideFraction);
    259         float endY = mSlideCalculator.getGoneY(sceneRoot, view, mSlideFraction);
    260         return TranslationAnimationCreator
    261                 .createAnimation(view, startValues, position[0], position[1],
    262                         startX, startY, endX, endY, sAccelerate, this);
    263     }
    264 
    265     /** @hide */
    266     public void setSlideFraction(float slideFraction) {
    267         mSlideFraction = slideFraction;
    268     }
    269 }
    270