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.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.accessibility.AccessibilityEvent;
     26 import android.view.accessibility.AccessibilityNodeInfo;
     27 import android.view.animation.Animation;
     28 import 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  */
     38 public 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      * Indicates whether the current View should be animated the first time
    333      * the ViewAnimation is displayed.
    334      *
    335      * @param animate True to animate the current View the first time it is displayed,
    336      *                false otherwise.
    337      */
    338     public void setAnimateFirstView(boolean animate) {
    339         mAnimateFirstTime = animate;
    340     }
    341 
    342     @Override
    343     public int getBaseline() {
    344         return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline();
    345     }
    346 
    347     @Override
    348     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
    349         super.onInitializeAccessibilityEvent(event);
    350         event.setClassName(ViewAnimator.class.getName());
    351     }
    352 
    353     @Override
    354     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
    355         super.onInitializeAccessibilityNodeInfo(info);
    356         info.setClassName(ViewAnimator.class.getName());
    357     }
    358 }
    359