/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.widget; import android.annotation.AnimRes; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationUtils; /** * Base class for a {@link FrameLayout} container that will perform animations * when switching between its views. * * @attr ref android.R.styleable#ViewAnimator_inAnimation * @attr ref android.R.styleable#ViewAnimator_outAnimation * @attr ref android.R.styleable#ViewAnimator_animateFirstView */ public class ViewAnimator extends FrameLayout { int mWhichChild = 0; boolean mFirstTime = true; boolean mAnimateFirstTime = true; Animation mInAnimation; Animation mOutAnimation; public ViewAnimator(Context context) { super(context); initViewAnimator(context, null); } public ViewAnimator(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ViewAnimator); int resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_inAnimation, 0); if (resource > 0) { setInAnimation(context, resource); } resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_outAnimation, 0); if (resource > 0) { setOutAnimation(context, resource); } boolean flag = a.getBoolean(com.android.internal.R.styleable.ViewAnimator_animateFirstView, true); setAnimateFirstView(flag); a.recycle(); initViewAnimator(context, attrs); } /** * Initialize this {@link ViewAnimator}, possibly setting * {@link #setMeasureAllChildren(boolean)} based on {@link FrameLayout} flags. */ private void initViewAnimator(Context context, AttributeSet attrs) { if (attrs == null) { // For compatibility, always measure children when undefined. mMeasureAllChildren = true; return; } // For compatibility, default to measure children, but allow XML // attribute to override. final TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout); final boolean measureAllChildren = a.getBoolean( com.android.internal.R.styleable.FrameLayout_measureAllChildren, true); setMeasureAllChildren(measureAllChildren); a.recycle(); } /** * Sets which child view will be displayed. * * @param whichChild the index of the child view to display */ @android.view.RemotableViewMethod public void setDisplayedChild(int whichChild) { mWhichChild = whichChild; if (whichChild >= getChildCount()) { mWhichChild = 0; } else if (whichChild < 0) { mWhichChild = getChildCount() - 1; } boolean hasFocus = getFocusedChild() != null; // This will clear old focus if we had it showOnly(mWhichChild); if (hasFocus) { // Try to retake focus if we had it requestFocus(FOCUS_FORWARD); } } /** * Returns the index of the currently displayed child view. */ public int getDisplayedChild() { return mWhichChild; } /** * Manually shows the next child. */ @android.view.RemotableViewMethod public void showNext() { setDisplayedChild(mWhichChild + 1); } /** * Manually shows the previous child. */ @android.view.RemotableViewMethod public void showPrevious() { setDisplayedChild(mWhichChild - 1); } /** * Shows only the specified child. The other displays Views exit the screen, * optionally with the with the {@link #getOutAnimation() out animation} and * the specified child enters the screen, optionally with the * {@link #getInAnimation() in animation}. * * @param childIndex The index of the child to be shown. * @param animate Whether or not to use the in and out animations, defaults * to true. */ void showOnly(int childIndex, boolean animate) { final int count = getChildCount(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (i == childIndex) { if (animate && mInAnimation != null) { child.startAnimation(mInAnimation); } child.setVisibility(View.VISIBLE); mFirstTime = false; } else { if (animate && mOutAnimation != null && child.getVisibility() == View.VISIBLE) { child.startAnimation(mOutAnimation); } else if (child.getAnimation() == mInAnimation) child.clearAnimation(); child.setVisibility(View.GONE); } } } /** * Shows only the specified child. The other displays Views exit the screen * with the {@link #getOutAnimation() out animation} and the specified child * enters the screen with the {@link #getInAnimation() in animation}. * * @param childIndex The index of the child to be shown. */ void showOnly(int childIndex) { final boolean animate = (!mFirstTime || mAnimateFirstTime); showOnly(childIndex, animate); } @Override public void addView(View child, int index, ViewGroup.LayoutParams params) { super.addView(child, index, params); if (getChildCount() == 1) { child.setVisibility(View.VISIBLE); } else { child.setVisibility(View.GONE); } if (index >= 0 && mWhichChild >= index) { // Added item above current one, increment the index of the displayed child setDisplayedChild(mWhichChild + 1); } } @Override public void removeAllViews() { super.removeAllViews(); mWhichChild = 0; mFirstTime = true; } @Override public void removeView(View view) { final int index = indexOfChild(view); if (index >= 0) { removeViewAt(index); } } @Override public void removeViewAt(int index) { super.removeViewAt(index); final int childCount = getChildCount(); if (childCount == 0) { mWhichChild = 0; mFirstTime = true; } else if (mWhichChild >= childCount) { // Displayed is above child count, so float down to top of stack setDisplayedChild(childCount - 1); } else if (mWhichChild == index) { // Displayed was removed, so show the new child living in its place setDisplayedChild(mWhichChild); } } public void removeViewInLayout(View view) { removeView(view); } public void removeViews(int start, int count) { super.removeViews(start, count); if (getChildCount() == 0) { mWhichChild = 0; mFirstTime = true; } else if (mWhichChild >= start && mWhichChild < start + count) { // Try showing new displayed child, wrapping if needed setDisplayedChild(mWhichChild); } } public void removeViewsInLayout(int start, int count) { removeViews(start, count); } /** * Returns the View corresponding to the currently displayed child. * * @return The View currently displayed. * * @see #getDisplayedChild() */ public View getCurrentView() { return getChildAt(mWhichChild); } /** * Returns the current animation used to animate a View that enters the screen. * * @return An Animation or null if none is set. * * @see #setInAnimation(android.view.animation.Animation) * @see #setInAnimation(android.content.Context, int) */ public Animation getInAnimation() { return mInAnimation; } /** * Specifies the animation used to animate a View that enters the screen. * * @param inAnimation The animation started when a View enters the screen. * * @see #getInAnimation() * @see #setInAnimation(android.content.Context, int) */ public void setInAnimation(Animation inAnimation) { mInAnimation = inAnimation; } /** * Returns the current animation used to animate a View that exits the screen. * * @return An Animation or null if none is set. * * @see #setOutAnimation(android.view.animation.Animation) * @see #setOutAnimation(android.content.Context, int) */ public Animation getOutAnimation() { return mOutAnimation; } /** * Specifies the animation used to animate a View that exit the screen. * * @param outAnimation The animation started when a View exit the screen. * * @see #getOutAnimation() * @see #setOutAnimation(android.content.Context, int) */ public void setOutAnimation(Animation outAnimation) { mOutAnimation = outAnimation; } /** * Specifies the animation used to animate a View that enters the screen. * * @param context The application's environment. * @param resourceID The resource id of the animation. * * @see #getInAnimation() * @see #setInAnimation(android.view.animation.Animation) */ public void setInAnimation(Context context, @AnimRes int resourceID) { setInAnimation(AnimationUtils.loadAnimation(context, resourceID)); } /** * Specifies the animation used to animate a View that exit the screen. * * @param context The application's environment. * @param resourceID The resource id of the animation. * * @see #getOutAnimation() * @see #setOutAnimation(android.view.animation.Animation) */ public void setOutAnimation(Context context, @AnimRes int resourceID) { setOutAnimation(AnimationUtils.loadAnimation(context, resourceID)); } /** * Returns whether the current View should be animated the first time the ViewAnimator * is displayed. * * @return true if the current View will be animated the first time it is displayed, * false otherwise. * * @see #setAnimateFirstView(boolean) */ public boolean getAnimateFirstView() { return mAnimateFirstTime; } /** * Indicates whether the current View should be animated the first time * the ViewAnimator is displayed. * * @param animate True to animate the current View the first time it is displayed, * false otherwise. */ public void setAnimateFirstView(boolean animate) { mAnimateFirstTime = animate; } @Override public int getBaseline() { return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline(); } @Override public CharSequence getAccessibilityClassName() { return ViewAnimator.class.getName(); } }