Home | History | Annotate | Download | only in widget
      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 
     47     public ViewAnimator(Context context) {
     48         super(context);
     49         initViewAnimator(context, null);
     50     }
     51 
     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      */
     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
    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      */
    120     public int getDisplayedChild() {
    121         return mWhichChild;
    122     }
    123 
    124     /**
    125      * Manually shows the next child.
    126      */
    127     @android.view.RemotableViewMethod
    128     public void showNext() {
    129         setDisplayedChild(mWhichChild + 1);
    130     }
    131 
    132     /**
    133      * Manually shows the previous child.
    134      */
    135     @android.view.RemotableViewMethod
    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      */
    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      */
    176     void showOnly(int childIndex) {
    177         final boolean animate = (!mFirstTime || mAnimateFirstTime);
    178         showOnly(childIndex, animate);
    179     }
    180 
    181     @Override
    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
    196     public void removeAllViews() {
    197         super.removeAllViews();
    198         mWhichChild = 0;
    199         mFirstTime = true;
    200     }
    201 
    202     @Override
    203     public void removeView(View view) {
    204         final int index = indexOfChild(view);
    205         if (index >= 0) {
    206             removeViewAt(index);
    207         }
    208     }
    209 
    210     @Override
    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 
    226     public void removeViewInLayout(View view) {
    227         removeView(view);
    228     }
    229 
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    350     public void setAnimateFirstView(boolean animate) {
    351         mAnimateFirstTime = animate;
    352     }
    353 
    354     @Override
    355     public int getBaseline() {
    356         return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline();
    357     }
    358 
    359     @Override
    360     public CharSequence getAccessibilityClassName() {
    361         return ViewAnimator.class.getName();
    362     }
    363 }
    364