ViewAnimator.java revision baea244a9fadd6fc30c1491085d01e1fe30f8ca4
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
17package android.widget;
18
19
20import android.content.Context;
21import android.content.res.TypedArray;
22import android.util.AttributeSet;
23import android.view.View;
24import android.view.ViewGroup;
25import android.view.animation.Animation;
26import 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 */
36public 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
46    public ViewAnimator(Context context) {
47        super(context);
48        initViewAnimator(context, null);
49    }
50
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     */
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
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     */
119    public int getDisplayedChild() {
120        return mWhichChild;
121    }
122
123    /**
124     * Manually shows the next child.
125     */
126    @android.view.RemotableViewMethod
127    public void showNext() {
128        setDisplayedChild(mWhichChild + 1);
129    }
130
131    /**
132     * Manually shows the previous child.
133     */
134    @android.view.RemotableViewMethod
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     */
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     */
175    void showOnly(int childIndex) {
176        final boolean animate = (!mFirstTime || mAnimateFirstTime);
177        showOnly(childIndex, animate);
178    }
179
180    @Override
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        if (index >= 0 && mWhichChild >= index) {
189            // Added item above current one, increment the index of the displayed child
190            setDisplayedChild(mWhichChild + 1);
191        }
192    }
193
194    @Override
195    public void removeAllViews() {
196        super.removeAllViews();
197        mWhichChild = 0;
198        mFirstTime = true;
199    }
200
201    @Override
202    public void removeView(View view) {
203        final int index = indexOfChild(view);
204        if (index >= 0) {
205            removeViewAt(index);
206        }
207    }
208
209    @Override
210    public void removeViewAt(int index) {
211        super.removeViewAt(index);
212        final int childCount = getChildCount();
213        if (childCount == 0) {
214            mWhichChild = 0;
215            mFirstTime = true;
216        } else if (mWhichChild >= childCount) {
217            // Displayed is above child count, so float down to top of stack
218            setDisplayedChild(childCount - 1);
219        } else if (mWhichChild == index) {
220            // Displayed was removed, so show the new child living in its place
221            setDisplayedChild(mWhichChild);
222        }
223    }
224
225    public void removeViewInLayout(View view) {
226        removeView(view);
227    }
228
229    public void removeViews(int start, int count) {
230        super.removeViews(start, count);
231        if (getChildCount() == 0) {
232            mWhichChild = 0;
233            mFirstTime = true;
234        } else if (mWhichChild >= start && mWhichChild < start + count) {
235            // Try showing new displayed child, wrapping if needed
236            setDisplayedChild(mWhichChild);
237        }
238    }
239
240    public void removeViewsInLayout(int start, int count) {
241        removeViews(start, count);
242    }
243
244    /**
245     * Returns the View corresponding to the currently displayed child.
246     *
247     * @return The View currently displayed.
248     *
249     * @see #getDisplayedChild()
250     */
251    public View getCurrentView() {
252        return getChildAt(mWhichChild);
253    }
254
255    /**
256     * Returns the current animation used to animate a View that enters the screen.
257     *
258     * @return An Animation or null if none is set.
259     *
260     * @see #setInAnimation(android.view.animation.Animation)
261     * @see #setInAnimation(android.content.Context, int)
262     */
263    public Animation getInAnimation() {
264        return mInAnimation;
265    }
266
267    /**
268     * Specifies the animation used to animate a View that enters the screen.
269     *
270     * @param inAnimation The animation started when a View enters the screen.
271     *
272     * @see #getInAnimation()
273     * @see #setInAnimation(android.content.Context, int)
274     */
275    public void setInAnimation(Animation inAnimation) {
276        mInAnimation = inAnimation;
277    }
278
279    /**
280     * Returns the current animation used to animate a View that exits the screen.
281     *
282     * @return An Animation or null if none is set.
283     *
284     * @see #setOutAnimation(android.view.animation.Animation)
285     * @see #setOutAnimation(android.content.Context, int)
286     */
287    public Animation getOutAnimation() {
288        return mOutAnimation;
289    }
290
291    /**
292     * Specifies the animation used to animate a View that exit the screen.
293     *
294     * @param outAnimation The animation started when a View exit the screen.
295     *
296     * @see #getOutAnimation()
297     * @see #setOutAnimation(android.content.Context, int)
298     */
299    public void setOutAnimation(Animation outAnimation) {
300        mOutAnimation = outAnimation;
301    }
302
303    /**
304     * Specifies the animation used to animate a View that enters the screen.
305     *
306     * @param context The application's environment.
307     * @param resourceID The resource id of the animation.
308     *
309     * @see #getInAnimation()
310     * @see #setInAnimation(android.view.animation.Animation)
311     */
312    public void setInAnimation(Context context, int resourceID) {
313        setInAnimation(AnimationUtils.loadAnimation(context, resourceID));
314    }
315
316    /**
317     * Specifies the animation used to animate a View that exit the screen.
318     *
319     * @param context The application's environment.
320     * @param resourceID The resource id of the animation.
321     *
322     * @see #getOutAnimation()
323     * @see #setOutAnimation(android.view.animation.Animation)
324     */
325    public void setOutAnimation(Context context, int resourceID) {
326        setOutAnimation(AnimationUtils.loadAnimation(context, resourceID));
327    }
328
329    /**
330     * Indicates whether the current View should be animated the first time
331     * the ViewAnimation is displayed.
332     *
333     * @param animate True to animate the current View the first time it is displayed,
334     *                false otherwise.
335     */
336    public void setAnimateFirstView(boolean animate) {
337        mAnimateFirstTime = animate;
338    }
339
340    @Override
341    public int getBaseline() {
342        return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline();
343    }
344}
345