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