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