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