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