Slide.java revision dc21d3b2804c24fe29ec860796d11185901364c4
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 */
16package android.transition;
17
18import android.animation.Animator;
19import android.animation.AnimatorListenerAdapter;
20import android.animation.AnimatorSet;
21import android.animation.ObjectAnimator;
22import android.animation.TimeInterpolator;
23import android.animation.ValueAnimator;
24import android.graphics.Rect;
25import android.util.Log;
26import android.util.Property;
27import android.view.Gravity;
28import android.view.View;
29import android.view.ViewGroup;
30import android.view.animation.AccelerateInterpolator;
31import android.view.animation.DecelerateInterpolator;
32
33/**
34 * This transition tracks changes to the visibility of target views in the
35 * start and end scenes and moves views in or out from one of the edges of the
36 * scene. Visibility is determined by both the
37 * {@link View#setVisibility(int)} state of the view as well as whether it
38 * is parented in the current view hierarchy. Disappearing Views are
39 * limited as described in {@link Visibility#onDisappear(android.view.ViewGroup,
40 * TransitionValues, int, TransitionValues, int)}.
41 */
42public class Slide extends Visibility {
43    private static final String TAG = "Slide";
44
45    private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
46    private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
47
48    private CalculateSlide mSlideCalculator = sCalculateBottom;
49
50    private interface CalculateSlide {
51        /** Returns the translation value for view when it out of the scene */
52        float getGone(ViewGroup sceneRoot, View view);
53
54        /** Returns the translation value for view when it is in the scene */
55        float getHere(View view);
56
57        /** Returns the property to animate translation */
58        Property<View, Float> getProperty();
59    }
60
61    private static abstract class CalculateSlideHorizontal implements CalculateSlide {
62        @Override
63        public float getHere(View view) {
64            return view.getTranslationX();
65        }
66
67        @Override
68        public Property<View, Float> getProperty() {
69            return View.TRANSLATION_X;
70        }
71    }
72
73    private static abstract class CalculateSlideVertical implements CalculateSlide {
74        @Override
75        public float getHere(View view) {
76            return view.getTranslationY();
77        }
78
79        @Override
80        public Property<View, Float> getProperty() {
81            return View.TRANSLATION_Y;
82        }
83    }
84
85    private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() {
86        @Override
87        public float getGone(ViewGroup sceneRoot, View view) {
88            return view.getTranslationX() - sceneRoot.getWidth();
89        }
90    };
91
92    private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
93        @Override
94        public float getGone(ViewGroup sceneRoot, View view) {
95            return view.getTranslationY() - sceneRoot.getHeight();
96        }
97    };
98
99    private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() {
100        @Override
101        public float getGone(ViewGroup sceneRoot, View view) {
102            return view.getTranslationX() + sceneRoot.getWidth();
103        }
104    };
105
106    private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
107        @Override
108        public float getGone(ViewGroup sceneRoot, View view) {
109            return view.getTranslationY() + sceneRoot.getHeight();
110        }
111    };
112
113    /**
114     * Constructor using the default {@link Gravity#BOTTOM}
115     * slide edge direction.
116     */
117    public Slide() {
118        setSlideEdge(Gravity.BOTTOM);
119    }
120
121    /**
122     * Constructor using the provided slide edge direction.
123     */
124    public Slide(int slideEdge) {
125        setSlideEdge(slideEdge);
126    }
127
128    /**
129     * Change the edge that Views appear and disappear from.
130     * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of
131     *                  {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
132     *                  {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}.
133     */
134    public void setSlideEdge(int slideEdge) {
135        switch (slideEdge) {
136            case Gravity.LEFT:
137                mSlideCalculator = sCalculateLeft;
138                break;
139            case Gravity.TOP:
140                mSlideCalculator = sCalculateTop;
141                break;
142            case Gravity.RIGHT:
143                mSlideCalculator = sCalculateRight;
144                break;
145            case Gravity.BOTTOM:
146                mSlideCalculator = sCalculateBottom;
147                break;
148            default:
149                throw new IllegalArgumentException("Invalid slide direction");
150        }
151        SidePropagation propagation = new SidePropagation();
152        propagation.setSide(slideEdge);
153        setPropagation(propagation);
154    }
155
156    private Animator createAnimation(final View view, Property<View, Float> property,
157            float start, float end, float terminalValue, TimeInterpolator interpolator) {
158        view.setTranslationY(start);
159        if (start == end) {
160            return null;
161        }
162        final ObjectAnimator anim = ObjectAnimator.ofFloat(view, property, start, end);
163
164        SlideAnimatorListener listener = new SlideAnimatorListener(view, terminalValue, end);
165        anim.addListener(listener);
166        anim.addPauseListener(listener);
167        anim.setInterpolator(interpolator);
168        return anim;
169    }
170
171    @Override
172    public Animator onAppear(ViewGroup sceneRoot, View view,
173            TransitionValues startValues, TransitionValues endValues) {
174        if (endValues == null) {
175            return null;
176        }
177        float end = mSlideCalculator.getHere(view);
178        float start = mSlideCalculator.getGone(sceneRoot, view);
179        return createAnimation(view, mSlideCalculator.getProperty(), start, end, end, sDecelerate);
180    }
181
182    @Override
183    public Animator onDisappear(ViewGroup sceneRoot, View view,
184            TransitionValues startValues, TransitionValues endValues) {
185        float start = mSlideCalculator.getHere(view);
186        float end = mSlideCalculator.getGone(sceneRoot, view);
187
188        return createAnimation(view, mSlideCalculator.getProperty(), start, end, start,
189                sAccelerate);
190    }
191
192    private static class SlideAnimatorListener extends AnimatorListenerAdapter {
193        private boolean mCanceled = false;
194        private float mPausedY;
195        private final View mView;
196        private final float mEndY;
197        private final float mTerminalY;
198
199        public SlideAnimatorListener(View view, float terminalY, float endY) {
200            mView = view;
201            mTerminalY = terminalY;
202            mEndY = endY;
203        }
204
205        @Override
206        public void onAnimationCancel(Animator animator) {
207            mView.setTranslationY(mTerminalY);
208            mCanceled = true;
209        }
210
211        @Override
212        public void onAnimationEnd(Animator animator) {
213            if (!mCanceled) {
214                mView.setTranslationY(mTerminalY);
215            }
216        }
217
218        @Override
219        public void onAnimationPause(Animator animator) {
220            mPausedY = mView.getTranslationY();
221            mView.setTranslationY(mEndY);
222        }
223
224        @Override
225        public void onAnimationResume(Animator animator) {
226            mView.setTranslationY(mPausedY);
227        }
228    }
229}
230