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