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