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