• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.graphics.Rect;
19 import android.util.FloatMath;
20 import android.util.Log;
21 import android.view.Gravity;
22 import android.view.View;
23 import android.view.ViewGroup;
24 
25 /**
26  * A <code>TransitionPropagation</code> that propagates based on the distance to the side
27  * and, orthogonally, the distance to epicenter. If the transitioning View is visible in
28  * the start of the transition, then it will transition sooner when closer to the side and
29  * later when farther. If the view is not visible in the start of the transition, then
30  * it will transition later when closer to the side and sooner when farther from the edge.
31  * This is the default TransitionPropagation used with {@link android.transition.Slide}.
32  */
33 public class SidePropagation extends VisibilityPropagation {
34     private static final String TAG = "SlidePropagation";
35 
36     private float mPropagationSpeed = 3.0f;
37     private int mSide = Gravity.BOTTOM;
38 
39     /**
40      * Sets the side that is used to calculate the transition propagation. If the transitioning
41      * View is visible in the start of the transition, then it will transition sooner when
42      * closer to the side and later when farther. If the view is not visible in the start of
43      * the transition, then it will transition later when closer to the side and sooner when
44      * farther from the edge. The default is {@link Gravity#BOTTOM}.
45      *
46      * @param side The side that is used to calculate the transition propagation. Must be one of
47      *             {@link Gravity#LEFT}, {@link Gravity#TOP}, {@link Gravity#RIGHT},
48      *             {@link Gravity#BOTTOM}, {@link Gravity#START}, or {@link Gravity#END}.
49      */
setSide(int side)50     public void setSide(int side) {
51         mSide = side;
52     }
53 
54     /**
55      * Sets the speed at which transition propagation happens, relative to the duration of the
56      * Transition. A <code>propagationSpeed</code> of 1 means that a View centered at the side
57      * set in {@link #setSide(int)} and View centered at the opposite edge will have a difference
58      * in start delay of approximately the duration of the Transition. A speed of 2 means the
59      * start delay difference will be approximately half of the duration of the transition. A
60      * value of 0 is illegal, but negative values will invert the propagation.
61      *
62      * @param propagationSpeed The speed at which propagation occurs, relative to the duration
63      *                         of the transition. A speed of 4 means it works 4 times as fast
64      *                         as the duration of the transition. May not be 0.
65      */
setPropagationSpeed(float propagationSpeed)66     public void setPropagationSpeed(float propagationSpeed) {
67         if (propagationSpeed == 0) {
68             throw new IllegalArgumentException("propagationSpeed may not be 0");
69         }
70         mPropagationSpeed = propagationSpeed;
71     }
72 
73     @Override
getStartDelay(ViewGroup sceneRoot, Transition transition, TransitionValues startValues, TransitionValues endValues)74     public long getStartDelay(ViewGroup sceneRoot, Transition transition,
75             TransitionValues startValues, TransitionValues endValues) {
76         if (startValues == null && endValues == null) {
77             return 0;
78         }
79         int directionMultiplier = 1;
80         Rect epicenter = transition.getEpicenter();
81         TransitionValues positionValues;
82         if (endValues == null || getViewVisibility(startValues) == View.VISIBLE) {
83             positionValues = startValues;
84             directionMultiplier = -1;
85         } else {
86             positionValues = endValues;
87         }
88 
89         int viewCenterX = getViewX(positionValues);
90         int viewCenterY = getViewY(positionValues);
91 
92         int[] loc = new int[2];
93         sceneRoot.getLocationOnScreen(loc);
94         int left = loc[0] + Math.round(sceneRoot.getTranslationX());
95         int top = loc[1] + Math.round(sceneRoot.getTranslationY());
96         int right = left + sceneRoot.getWidth();
97         int bottom = top + sceneRoot.getHeight();
98 
99         int epicenterX;
100         int epicenterY;
101         if (epicenter != null) {
102             epicenterX = epicenter.centerX();
103             epicenterY = epicenter.centerY();
104         } else {
105             epicenterX = (left + right) / 2;
106             epicenterY = (top + bottom) / 2;
107         }
108 
109         float distance = distance(sceneRoot, viewCenterX, viewCenterY, epicenterX, epicenterY,
110                 left, top, right, bottom);
111         float maxDistance = getMaxDistance(sceneRoot);
112         float distanceFraction = distance/maxDistance;
113 
114         long duration = transition.getDuration();
115         if (duration < 0) {
116             duration = 300;
117         }
118 
119         return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction);
120     }
121 
distance(View sceneRoot, int viewX, int viewY, int epicenterX, int epicenterY, int left, int top, int right, int bottom)122     private int distance(View sceneRoot, int viewX, int viewY, int epicenterX, int epicenterY,
123             int left, int top, int right, int bottom) {
124         final int side;
125         if (mSide == Gravity.START) {
126             final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
127             side = isRtl ? Gravity.RIGHT : Gravity.LEFT;
128         } else if (mSide == Gravity.END) {
129             final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
130             side = isRtl ? Gravity.LEFT : Gravity.RIGHT;
131         } else {
132             side = mSide;
133         }
134         int distance = 0;
135         switch (side) {
136             case Gravity.LEFT:
137                 distance = right - viewX + Math.abs(epicenterY - viewY);
138                 break;
139             case Gravity.TOP:
140                 distance = bottom - viewY + Math.abs(epicenterX - viewX);
141                 break;
142             case Gravity.RIGHT:
143                 distance = viewX - left + Math.abs(epicenterY - viewY);
144                 break;
145             case Gravity.BOTTOM:
146                 distance = viewY - top + Math.abs(epicenterX - viewX);
147                 break;
148         }
149         return distance;
150     }
151 
getMaxDistance(ViewGroup sceneRoot)152     private int getMaxDistance(ViewGroup sceneRoot) {
153         switch (mSide) {
154             case Gravity.LEFT:
155             case Gravity.RIGHT:
156             case Gravity.START:
157             case Gravity.END:
158                 return sceneRoot.getWidth();
159             default:
160                 return sceneRoot.getHeight();
161         }
162     }
163 }
164