Home | History | Annotate | Download | only in transition
      1 /*
      2  * Copyright (C) 2013 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 android.transition;
     18 
     19 import android.animation.Animator;
     20 import android.view.View;
     21 import android.view.ViewGroup;
     22 
     23 /**
     24  * This transition tracks changes to the visibility of target views in the
     25  * start and end scenes. Visibility is determined not just by the
     26  * {@link View#setVisibility(int)} state of views, but also whether
     27  * views exist in the current view hierarchy. The class is intended to be a
     28  * utility for subclasses such as {@link Fade}, which use this visibility
     29  * information to determine the specific animations to run when visibility
     30  * changes occur. Subclasses should implement one or both of the methods
     31  * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)},
     32  * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)},
     33  */
     34 public abstract class Visibility extends Transition {
     35 
     36     private static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
     37     private static final String PROPNAME_PARENT = "android:visibility:parent";
     38     private static final String[] sTransitionProperties = {
     39             PROPNAME_VISIBILITY,
     40             PROPNAME_PARENT,
     41     };
     42 
     43     private static class VisibilityInfo {
     44         boolean visibilityChange;
     45         boolean fadeIn;
     46         int startVisibility;
     47         int endVisibility;
     48         ViewGroup startParent;
     49         ViewGroup endParent;
     50     }
     51 
     52     @Override
     53     public String[] getTransitionProperties() {
     54         return sTransitionProperties;
     55     }
     56 
     57     private void captureValues(TransitionValues transitionValues) {
     58         int visibility = transitionValues.view.getVisibility();
     59         transitionValues.values.put(PROPNAME_VISIBILITY, visibility);
     60         transitionValues.values.put(PROPNAME_PARENT, transitionValues.view.getParent());
     61     }
     62 
     63     @Override
     64     public void captureStartValues(TransitionValues transitionValues) {
     65         captureValues(transitionValues);
     66     }
     67 
     68     @Override
     69     public void captureEndValues(TransitionValues transitionValues) {
     70         captureValues(transitionValues);
     71     }
     72 
     73     /**
     74      * Returns whether the view is 'visible' according to the given values
     75      * object. This is determined by testing the same properties in the values
     76      * object that are used to determine whether the object is appearing or
     77      * disappearing in the {@link
     78      * Transition#createAnimator(ViewGroup, TransitionValues, TransitionValues)}
     79      * method. This method can be called by, for example, subclasses that want
     80      * to know whether the object is visible in the same way that Visibility
     81      * determines it for the actual animation.
     82      *
     83      * @param values The TransitionValues object that holds the information by
     84      * which visibility is determined.
     85      * @return True if the view reference by <code>values</code> is visible,
     86      * false otherwise.
     87      */
     88     public boolean isVisible(TransitionValues values) {
     89         if (values == null) {
     90             return false;
     91         }
     92         int visibility = (Integer) values.values.get(PROPNAME_VISIBILITY);
     93         View parent = (View) values.values.get(PROPNAME_PARENT);
     94 
     95         return visibility == View.VISIBLE && parent != null;
     96     }
     97 
     98     private VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues,
     99             TransitionValues endValues) {
    100         final VisibilityInfo visInfo = new VisibilityInfo();
    101         visInfo.visibilityChange = false;
    102         visInfo.fadeIn = false;
    103         if (startValues != null) {
    104             visInfo.startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
    105             visInfo.startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
    106         } else {
    107             visInfo.startVisibility = -1;
    108             visInfo.startParent = null;
    109         }
    110         if (endValues != null) {
    111             visInfo.endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
    112             visInfo.endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
    113         } else {
    114             visInfo.endVisibility = -1;
    115             visInfo.endParent = null;
    116         }
    117         if (startValues != null && endValues != null) {
    118             if (visInfo.startVisibility == visInfo.endVisibility &&
    119                     visInfo.startParent == visInfo.endParent) {
    120                 return visInfo;
    121             } else {
    122                 if (visInfo.startVisibility != visInfo.endVisibility) {
    123                     if (visInfo.startVisibility == View.VISIBLE) {
    124                         visInfo.fadeIn = false;
    125                         visInfo.visibilityChange = true;
    126                     } else if (visInfo.endVisibility == View.VISIBLE) {
    127                         visInfo.fadeIn = true;
    128                         visInfo.visibilityChange = true;
    129                     }
    130                     // no visibilityChange if going between INVISIBLE and GONE
    131                 } else if (visInfo.startParent != visInfo.endParent) {
    132                     if (visInfo.endParent == null) {
    133                         visInfo.fadeIn = false;
    134                         visInfo.visibilityChange = true;
    135                     } else if (visInfo.startParent == null) {
    136                         visInfo.fadeIn = true;
    137                         visInfo.visibilityChange = true;
    138                     }
    139                 }
    140             }
    141         }
    142         if (startValues == null) {
    143             visInfo.fadeIn = true;
    144             visInfo.visibilityChange = true;
    145         } else if (endValues == null) {
    146             visInfo.fadeIn = false;
    147             visInfo.visibilityChange = true;
    148         }
    149         return visInfo;
    150     }
    151 
    152     @Override
    153     public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
    154             TransitionValues endValues) {
    155         VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues);
    156         if (visInfo.visibilityChange) {
    157             // Only transition views that are either targets of this transition
    158             // or whose parent hierarchies remain stable between scenes
    159             boolean isTarget = false;
    160             if (mTargets.size() > 0 || mTargetIds.size() > 0) {
    161                 View startView = startValues != null ? startValues.view : null;
    162                 View endView = endValues != null ? endValues.view : null;
    163                 int startId = startView != null ? startView.getId() : -1;
    164                 int endId = endView != null ? endView.getId() : -1;
    165                 isTarget = isValidTarget(startView, startId) || isValidTarget(endView, endId);
    166             }
    167             if (isTarget || ((visInfo.startParent != null || visInfo.endParent != null))) {
    168                 if (visInfo.fadeIn) {
    169                     return onAppear(sceneRoot, startValues, visInfo.startVisibility,
    170                             endValues, visInfo.endVisibility);
    171                 } else {
    172                     return onDisappear(sceneRoot, startValues, visInfo.startVisibility,
    173                             endValues, visInfo.endVisibility
    174                     );
    175                 }
    176             }
    177         }
    178         return null;
    179     }
    180 
    181     /**
    182      * The default implementation of this method does nothing. Subclasses
    183      * should override if they need to create an Animator when targets appear.
    184      * The method should only be called by the Visibility class; it is
    185      * not intended to be called from external classes.
    186      *
    187      * @param sceneRoot The root of the transition hierarchy
    188      * @param startValues The target values in the start scene
    189      * @param startVisibility The target visibility in the start scene
    190      * @param endValues The target values in the end scene
    191      * @param endVisibility The target visibility in the end scene
    192      * @return An Animator to be started at the appropriate time in the
    193      * overall transition for this scene change. A null value means no animation
    194      * should be run.
    195      */
    196     public Animator onAppear(ViewGroup sceneRoot,
    197             TransitionValues startValues, int startVisibility,
    198             TransitionValues endValues, int endVisibility) {
    199         return null;
    200     }
    201 
    202     /**
    203      * The default implementation of this method does nothing. Subclasses
    204      * should override if they need to create an Animator when targets disappear.
    205      * The method should only be called by the Visibility class; it is
    206      * not intended to be called from external classes.
    207      *
    208      *
    209      * @param sceneRoot The root of the transition hierarchy
    210      * @param startValues The target values in the start scene
    211      * @param startVisibility The target visibility in the start scene
    212      * @param endValues The target values in the end scene
    213      * @param endVisibility The target visibility in the end scene
    214      * @return An Animator to be started at the appropriate time in the
    215      * overall transition for this scene change. A null value means no animation
    216      * should be run.
    217      */
    218     public Animator onDisappear(ViewGroup sceneRoot,
    219             TransitionValues startValues, int startVisibility,
    220             TransitionValues endValues, int endVisibility) {
    221         return null;
    222     }
    223 }
    224