1 /* 2 * Copyright (C) 2006 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.widget; 18 19 20 import android.content.Context; 21 import android.content.res.TypedArray; 22 import android.util.AttributeSet; 23 import android.view.View; 24 import android.view.ViewGroup; 25 import android.view.animation.Animation; 26 import android.view.animation.AnimationUtils; 27 28 /** 29 * Base class for a {@link FrameLayout} container that will perform animations 30 * when switching between its views. 31 * 32 * @attr ref android.R.styleable#ViewAnimator_inAnimation 33 * @attr ref android.R.styleable#ViewAnimator_outAnimation 34 */ 35 public class ViewAnimator extends FrameLayout { 36 37 int mWhichChild = 0; 38 boolean mFirstTime = true; 39 boolean mAnimateFirstTime = true; 40 41 Animation mInAnimation; 42 Animation mOutAnimation; 43 44 public ViewAnimator(Context context) { 45 super(context); 46 initViewAnimator(context, null); 47 } 48 49 public ViewAnimator(Context context, AttributeSet attrs) { 50 super(context, attrs); 51 52 TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ViewAnimator); 53 int resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_inAnimation, 0); 54 if (resource > 0) { 55 setInAnimation(context, resource); 56 } 57 58 resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_outAnimation, 0); 59 if (resource > 0) { 60 setOutAnimation(context, resource); 61 } 62 a.recycle(); 63 64 initViewAnimator(context, attrs); 65 } 66 67 /** 68 * Initialize this {@link ViewAnimator}, possibly setting 69 * {@link #setMeasureAllChildren(boolean)} based on {@link FrameLayout} flags. 70 */ 71 private void initViewAnimator(Context context, AttributeSet attrs) { 72 if (attrs == null) { 73 // For compatibility, always measure children when undefined. 74 mMeasureAllChildren = true; 75 return; 76 } 77 78 // For compatibility, default to measure children, but allow XML 79 // attribute to override. 80 final TypedArray a = context.obtainStyledAttributes(attrs, 81 com.android.internal.R.styleable.FrameLayout); 82 final boolean measureAllChildren = a.getBoolean( 83 com.android.internal.R.styleable.FrameLayout_measureAllChildren, true); 84 setMeasureAllChildren(measureAllChildren); 85 a.recycle(); 86 } 87 88 /** 89 * Sets which child view will be displayed. 90 * 91 * @param whichChild the index of the child view to display 92 */ 93 public void setDisplayedChild(int whichChild) { 94 mWhichChild = whichChild; 95 if (whichChild >= getChildCount()) { 96 mWhichChild = 0; 97 } else if (whichChild < 0) { 98 mWhichChild = getChildCount() - 1; 99 } 100 boolean hasFocus = getFocusedChild() != null; 101 // This will clear old focus if we had it 102 showOnly(mWhichChild); 103 if (hasFocus) { 104 // Try to retake focus if we had it 105 requestFocus(FOCUS_FORWARD); 106 } 107 } 108 109 /** 110 * Returns the index of the currently displayed child view. 111 */ 112 public int getDisplayedChild() { 113 return mWhichChild; 114 } 115 116 /** 117 * Manually shows the next child. 118 */ 119 public void showNext() { 120 setDisplayedChild(mWhichChild + 1); 121 } 122 123 /** 124 * Manually shows the previous child. 125 */ 126 public void showPrevious() { 127 setDisplayedChild(mWhichChild - 1); 128 } 129 130 /** 131 * Shows only the specified child. The other displays Views exit the screen 132 * with the {@link #getOutAnimation() out animation} and the specified child 133 * enters the screen with the {@link #getInAnimation() in animation}. 134 * 135 * @param childIndex The index of the child to be shown. 136 */ 137 void showOnly(int childIndex) { 138 final int count = getChildCount(); 139 for (int i = 0; i < count; i++) { 140 final View child = getChildAt(i); 141 final boolean checkForFirst = (!mFirstTime || mAnimateFirstTime); 142 if (i == childIndex) { 143 if (checkForFirst && mInAnimation != null) { 144 child.startAnimation(mInAnimation); 145 } 146 child.setVisibility(View.VISIBLE); 147 mFirstTime = false; 148 } else { 149 if (checkForFirst && mOutAnimation != null && child.getVisibility() == View.VISIBLE) { 150 child.startAnimation(mOutAnimation); 151 } else if (child.getAnimation() == mInAnimation) 152 child.clearAnimation(); 153 child.setVisibility(View.GONE); 154 } 155 } 156 } 157 158 @Override 159 public void addView(View child, int index, ViewGroup.LayoutParams params) { 160 super.addView(child, index, params); 161 if (getChildCount() == 1) { 162 child.setVisibility(View.VISIBLE); 163 } else { 164 child.setVisibility(View.GONE); 165 } 166 } 167 168 @Override 169 public void removeAllViews() { 170 super.removeAllViews(); 171 mWhichChild = 0; 172 mFirstTime = true; 173 } 174 175 @Override 176 public void removeView(View view) { 177 final int index = indexOfChild(view); 178 if (index >= 0) { 179 removeViewAt(index); 180 } 181 } 182 183 @Override 184 public void removeViewAt(int index) { 185 super.removeViewAt(index); 186 final int childCount = getChildCount(); 187 if (childCount == 0) { 188 mWhichChild = 0; 189 mFirstTime = true; 190 } else if (mWhichChild >= childCount) { 191 // Displayed is above child count, so float down to top of stack 192 setDisplayedChild(childCount - 1); 193 } else if (mWhichChild == index) { 194 // Displayed was removed, so show the new child living in its place 195 setDisplayedChild(mWhichChild); 196 } 197 } 198 199 public void removeViewInLayout(View view) { 200 removeView(view); 201 } 202 203 public void removeViews(int start, int count) { 204 super.removeViews(start, count); 205 if (getChildCount() == 0) { 206 mWhichChild = 0; 207 mFirstTime = true; 208 } else if (mWhichChild >= start && mWhichChild < start + count) { 209 // Try showing new displayed child, wrapping if needed 210 setDisplayedChild(mWhichChild); 211 } 212 } 213 214 public void removeViewsInLayout(int start, int count) { 215 removeViews(start, count); 216 } 217 218 /** 219 * Returns the View corresponding to the currently displayed child. 220 * 221 * @return The View currently displayed. 222 * 223 * @see #getDisplayedChild() 224 */ 225 public View getCurrentView() { 226 return getChildAt(mWhichChild); 227 } 228 229 /** 230 * Returns the current animation used to animate a View that enters the screen. 231 * 232 * @return An Animation or null if none is set. 233 * 234 * @see #setInAnimation(android.view.animation.Animation) 235 * @see #setInAnimation(android.content.Context, int) 236 */ 237 public Animation getInAnimation() { 238 return mInAnimation; 239 } 240 241 /** 242 * Specifies the animation used to animate a View that enters the screen. 243 * 244 * @param inAnimation The animation started when a View enters the screen. 245 * 246 * @see #getInAnimation() 247 * @see #setInAnimation(android.content.Context, int) 248 */ 249 public void setInAnimation(Animation inAnimation) { 250 mInAnimation = inAnimation; 251 } 252 253 /** 254 * Returns the current animation used to animate a View that exits the screen. 255 * 256 * @return An Animation or null if none is set. 257 * 258 * @see #setOutAnimation(android.view.animation.Animation) 259 * @see #setOutAnimation(android.content.Context, int) 260 */ 261 public Animation getOutAnimation() { 262 return mOutAnimation; 263 } 264 265 /** 266 * Specifies the animation used to animate a View that exit the screen. 267 * 268 * @param outAnimation The animation started when a View exit the screen. 269 * 270 * @see #getOutAnimation() 271 * @see #setOutAnimation(android.content.Context, int) 272 */ 273 public void setOutAnimation(Animation outAnimation) { 274 mOutAnimation = outAnimation; 275 } 276 277 /** 278 * Specifies the animation used to animate a View that enters the screen. 279 * 280 * @param context The application's environment. 281 * @param resourceID The resource id of the animation. 282 * 283 * @see #getInAnimation() 284 * @see #setInAnimation(android.view.animation.Animation) 285 */ 286 public void setInAnimation(Context context, int resourceID) { 287 setInAnimation(AnimationUtils.loadAnimation(context, resourceID)); 288 } 289 290 /** 291 * Specifies the animation used to animate a View that exit the screen. 292 * 293 * @param context The application's environment. 294 * @param resourceID The resource id of the animation. 295 * 296 * @see #getOutAnimation() 297 * @see #setOutAnimation(android.view.animation.Animation) 298 */ 299 public void setOutAnimation(Context context, int resourceID) { 300 setOutAnimation(AnimationUtils.loadAnimation(context, resourceID)); 301 } 302 303 /** 304 * Indicates whether the current View should be animated the first time 305 * the ViewAnimation is displayed. 306 * 307 * @param animate True to animate the current View the first time it is displayed, 308 * false otherwise. 309 */ 310 public void setAnimateFirstView(boolean animate) { 311 mAnimateFirstTime = animate; 312 } 313 314 @Override 315 public int getBaseline() { 316 return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline(); 317 } 318 } 319