• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 
17 package androidx.transition;
18 
19 import android.animation.Animator;
20 import android.animation.TimeInterpolator;
21 import android.content.Context;
22 import android.graphics.Rect;
23 import android.util.AttributeSet;
24 import android.view.View;
25 import android.view.ViewGroup;
26 import android.view.animation.AccelerateInterpolator;
27 import android.view.animation.DecelerateInterpolator;
28 
29 import androidx.annotation.NonNull;
30 
31 /**
32  * This transition tracks changes to the visibility of target views in the
33  * start and end scenes and moves views in or out from the edges of the
34  * scene. Visibility is determined by both the
35  * {@link View#setVisibility(int)} state of the view as well as whether it
36  * is parented in the current view hierarchy. Disappearing Views are
37  * limited as described in {@link Visibility#onDisappear(android.view.ViewGroup,
38  * TransitionValues, int, TransitionValues, int)}.
39  * <p>Views move away from the focal View or the center of the Scene if
40  * no epicenter was provided.</p>
41  */
42 public class Explode extends Visibility {
43 
44     private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
45     private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
46     private static final String PROPNAME_SCREEN_BOUNDS = "android:explode:screenBounds";
47 
48     private int[] mTempLoc = new int[2];
49 
Explode()50     public Explode() {
51         setPropagation(new CircularPropagation());
52     }
53 
Explode(Context context, AttributeSet attrs)54     public Explode(Context context, AttributeSet attrs) {
55         super(context, attrs);
56         setPropagation(new CircularPropagation());
57     }
58 
captureValues(TransitionValues transitionValues)59     private void captureValues(TransitionValues transitionValues) {
60         View view = transitionValues.view;
61         view.getLocationOnScreen(mTempLoc);
62         int left = mTempLoc[0];
63         int top = mTempLoc[1];
64         int right = left + view.getWidth();
65         int bottom = top + view.getHeight();
66         transitionValues.values.put(PROPNAME_SCREEN_BOUNDS, new Rect(left, top, right, bottom));
67     }
68 
69     @Override
captureStartValues(@onNull TransitionValues transitionValues)70     public void captureStartValues(@NonNull TransitionValues transitionValues) {
71         super.captureStartValues(transitionValues);
72         captureValues(transitionValues);
73     }
74 
75     @Override
captureEndValues(@onNull TransitionValues transitionValues)76     public void captureEndValues(@NonNull TransitionValues transitionValues) {
77         super.captureEndValues(transitionValues);
78         captureValues(transitionValues);
79     }
80 
81     @Override
onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)82     public Animator onAppear(ViewGroup sceneRoot, View view,
83             TransitionValues startValues, TransitionValues endValues) {
84         if (endValues == null) {
85             return null;
86         }
87         Rect bounds = (Rect) endValues.values.get(PROPNAME_SCREEN_BOUNDS);
88         float endX = view.getTranslationX();
89         float endY = view.getTranslationY();
90         calculateOut(sceneRoot, bounds, mTempLoc);
91         float startX = endX + mTempLoc[0];
92         float startY = endY + mTempLoc[1];
93 
94         return TranslationAnimationCreator.createAnimation(view, endValues, bounds.left, bounds.top,
95                 startX, startY, endX, endY, sDecelerate);
96     }
97 
98     @Override
onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)99     public Animator onDisappear(ViewGroup sceneRoot, View view,
100             TransitionValues startValues, TransitionValues endValues) {
101         if (startValues == null) {
102             return null;
103         }
104         Rect bounds = (Rect) startValues.values.get(PROPNAME_SCREEN_BOUNDS);
105         int viewPosX = bounds.left;
106         int viewPosY = bounds.top;
107         float startX = view.getTranslationX();
108         float startY = view.getTranslationY();
109         float endX = startX;
110         float endY = startY;
111         int[] interruptedPosition = (int[]) startValues.view.getTag(R.id.transition_position);
112         if (interruptedPosition != null) {
113             // We want to have the end position relative to the interrupted position, not
114             // the position it was supposed to start at.
115             endX += interruptedPosition[0] - bounds.left;
116             endY += interruptedPosition[1] - bounds.top;
117             bounds.offsetTo(interruptedPosition[0], interruptedPosition[1]);
118         }
119         calculateOut(sceneRoot, bounds, mTempLoc);
120         endX += mTempLoc[0];
121         endY += mTempLoc[1];
122 
123         return TranslationAnimationCreator.createAnimation(view, startValues,
124                 viewPosX, viewPosY, startX, startY, endX, endY, sAccelerate);
125     }
126 
calculateOut(View sceneRoot, Rect bounds, int[] outVector)127     private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) {
128         sceneRoot.getLocationOnScreen(mTempLoc);
129         int sceneRootX = mTempLoc[0];
130         int sceneRootY = mTempLoc[1];
131         int focalX;
132         int focalY;
133 
134         Rect epicenter = getEpicenter();
135         if (epicenter == null) {
136             focalX = sceneRootX + (sceneRoot.getWidth() / 2)
137                     + Math.round(sceneRoot.getTranslationX());
138             focalY = sceneRootY + (sceneRoot.getHeight() / 2)
139                     + Math.round(sceneRoot.getTranslationY());
140         } else {
141             focalX = epicenter.centerX();
142             focalY = epicenter.centerY();
143         }
144 
145         int centerX = bounds.centerX();
146         int centerY = bounds.centerY();
147         float xVector = centerX - focalX;
148         float yVector = centerY - focalY;
149 
150         if (xVector == 0 && yVector == 0) {
151             // Random direction when View is centered on focal View.
152             xVector = (float) (Math.random() * 2) - 1;
153             yVector = (float) (Math.random() * 2) - 1;
154         }
155         float vectorSize = calculateDistance(xVector, yVector);
156         xVector /= vectorSize;
157         yVector /= vectorSize;
158 
159         float maxDistance =
160                 calculateMaxDistance(sceneRoot, focalX - sceneRootX, focalY - sceneRootY);
161 
162         outVector[0] = Math.round(maxDistance * xVector);
163         outVector[1] = Math.round(maxDistance * yVector);
164     }
165 
calculateMaxDistance(View sceneRoot, int focalX, int focalY)166     private static float calculateMaxDistance(View sceneRoot, int focalX, int focalY) {
167         int maxX = Math.max(focalX, sceneRoot.getWidth() - focalX);
168         int maxY = Math.max(focalY, sceneRoot.getHeight() - focalY);
169         return calculateDistance(maxX, maxY);
170     }
171 
calculateDistance(float x, float y)172     private static float calculateDistance(float x, float y) {
173         return (float) Math.sqrt((x * x) + (y * y));
174     }
175 
176 }
177