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 sCalculateTop = new CalculateSlideVertical() {
     80         @Override
     81         public float getGoneY(ViewGroup sceneRoot, View view) {
     82             return view.getTranslationY() - sceneRoot.getHeight();
     83         }
     84     };
     85 
     86     private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() {
     87         @Override
     88         public float getGoneX(ViewGroup sceneRoot, View view) {
     89             return view.getTranslationX() + sceneRoot.getWidth();
     90         }
     91     };
     92 
     93     private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
     94         @Override
     95         public float getGoneY(ViewGroup sceneRoot, View view) {
     96             return view.getTranslationY() + sceneRoot.getHeight();
     97         }
     98     };
     99 
    100     /**
    101      * Constructor using the default {@link Gravity#BOTTOM}
    102      * slide edge direction.
    103      */
    104     public Slide() {
    105         setSlideEdge(Gravity.BOTTOM);
    106     }
    107 
    108     /**
    109      * Constructor using the provided slide edge direction.
    110      */
    111     public Slide(int slideEdge) {
    112         setSlideEdge(slideEdge);
    113     }
    114 
    115     public Slide(Context context, AttributeSet attrs) {
    116         super(context, attrs);
    117         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Slide);
    118         int edge = a.getInt(R.styleable.Slide_slideEdge, Gravity.BOTTOM);
    119         a.recycle();
    120         setSlideEdge(edge);
    121     }
    122 
    123     private void captureValues(TransitionValues transitionValues) {
    124         View view = transitionValues.view;
    125         int[] position = new int[2];
    126         view.getLocationOnScreen(position);
    127         transitionValues.values.put(PROPNAME_SCREEN_POSITION, position);
    128     }
    129 
    130     @Override
    131     public void captureStartValues(TransitionValues transitionValues) {
    132         super.captureStartValues(transitionValues);
    133         captureValues(transitionValues);
    134     }
    135 
    136     @Override
    137     public void captureEndValues(TransitionValues transitionValues) {
    138         super.captureEndValues(transitionValues);
    139         captureValues(transitionValues);
    140     }
    141 
    142     /**
    143      * Change the edge that Views appear and disappear from.
    144      *
    145      * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of
    146      *                  {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
    147      *                  {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}.
    148      * @attr ref android.R.styleable#Slide_slideEdge
    149      */
    150     public void setSlideEdge(int slideEdge) {
    151         switch (slideEdge) {
    152             case Gravity.LEFT:
    153                 mSlideCalculator = sCalculateLeft;
    154                 break;
    155             case Gravity.TOP:
    156                 mSlideCalculator = sCalculateTop;
    157                 break;
    158             case Gravity.RIGHT:
    159                 mSlideCalculator = sCalculateRight;
    160                 break;
    161             case Gravity.BOTTOM:
    162                 mSlideCalculator = sCalculateBottom;
    163                 break;
    164             default:
    165                 throw new IllegalArgumentException("Invalid slide direction");
    166         }
    167         mSlideEdge = slideEdge;
    168         SidePropagation propagation = new SidePropagation();
    169         propagation.setSide(slideEdge);
    170         setPropagation(propagation);
    171     }
    172 
    173     /**
    174      * Returns the edge that Views appear and disappear from.
    175      *
    176      * @return the edge of the scene to use for Views appearing and disappearing. One of
    177      *         {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
    178      *         {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}.
    179      * @attr ref android.R.styleable#Slide_slideEdge
    180      */
    181     public int getSlideEdge() {
    182         return mSlideEdge;
    183     }
    184 
    185     @Override
    186     public Animator onAppear(ViewGroup sceneRoot, View view,
    187             TransitionValues startValues, TransitionValues endValues) {
    188         if (endValues == null) {
    189             return null;
    190         }
    191         int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION);
    192         float endX = view.getTranslationX();
    193         float endY = view.getTranslationY();
    194         float startX = mSlideCalculator.getGoneX(sceneRoot, view);
    195         float startY = mSlideCalculator.getGoneY(sceneRoot, view);
    196         return TranslationAnimationCreator
    197                 .createAnimation(view, endValues, position[0], position[1],
    198                         startX, startY, endX, endY, sDecelerate);
    199     }
    200 
    201     @Override
    202     public Animator onDisappear(ViewGroup sceneRoot, View view,
    203             TransitionValues startValues, TransitionValues endValues) {
    204         if (startValues == null) {
    205             return null;
    206         }
    207         int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION);
    208         float startX = view.getTranslationX();
    209         float startY = view.getTranslationY();
    210         float endX = mSlideCalculator.getGoneX(sceneRoot, view);
    211         float endY = mSlideCalculator.getGoneY(sceneRoot, view);
    212         return TranslationAnimationCreator
    213                 .createAnimation(view, startValues, position[0], position[1],
    214                         startX, startY, endX, endY, sAccelerate);
    215     }
    216 }
    217