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