AdapterViewAnimator.java revision 5b53f9186e7812c93bc578d18e92cb123481fcbc
13db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen/*
23db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Copyright (C) 2010 The Android Open Source Project
33db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen *
43db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Licensed under the Apache License, Version 2.0 (the "License");
53db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * you may not use this file except in compliance with the License.
63db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * You may obtain a copy of the License at
73db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen *
83db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen *      http://www.apache.org/licenses/LICENSE-2.0
93db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen *
103db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Unless required by applicable law or agreed to in writing, software
113db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * distributed under the License is distributed on an "AS IS" BASIS,
123db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * See the License for the specific language governing permissions and
143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * limitations under the License.
153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */
163db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
173db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenpackage android.widget;
183db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport java.util.ArrayList;
2044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
2144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.animation.PropertyAnimator;
223db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.content.Context;
233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.content.Intent;
243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.content.res.TypedArray;
2544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.graphics.Rect;
263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.os.Handler;
273db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.os.Looper;
283db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.util.AttributeSet;
293db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.view.View;
3044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.ViewGroup;
313db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.view.animation.Animation;
323db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.view.animation.AnimationUtils;
333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
343db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen/**
353db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Base class for a {@link AdapterView} that will perform animations
363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * when switching between its views.
373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen *
383db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_inAnimation
393db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_outAnimation
403db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_animateFirstView
413db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */
4244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenpublic abstract class AdapterViewAnimator extends AdapterView<Adapter>
4344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        implements RemoteViewsAdapter.RemoteAdapterConnectionCallback{
443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    private static final String TAG = "RemoteViewAnimator";
453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
4744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The index of the current child, which appears anywhere from the beginning
4844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * to the end of the current set of children, as specified by {@link #mActiveOffset}
4944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    int mWhichChild = 0;
5144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
5244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
5344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Whether or not the first view(s) should be animated in
5444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    boolean mAnimateFirstTime = true;
563db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
5744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
5844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *  Represents where the in the current window of
5944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *  views the current <code>mDisplayedChild</code> sits
6044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
6144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mActiveOffset = 0;
6244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
6344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
6444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The number of views that the {@link AdapterViewAnimator} keeps as children at any
6544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * given time (not counting views that are pending removal, see {@link #mPreviousViews}).
6644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
6744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mNumActiveViews = 1;
6844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
6944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
7044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Array of the children of the {@link AdapterViewAnimator}. This array
7144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * is accessed in a circular fashion
7244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
7344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    View[] mActiveViews;
7444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
7544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
7644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * List of views pending removal from the {@link AdapterViewAnimator}
7744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
7844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    ArrayList<View> mPreviousViews;
7944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
8044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
8144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The index, relative to the adapter, of the beginning of the window of views
8244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
8344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mCurrentWindowStart = 0;
8444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
8544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
8644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The index, relative to the adapter, of the end of the window of views
8744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
8844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mCurrentWindowEnd = -1;
8944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
9044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
9144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The same as {@link #mCurrentWindowStart}, except when the we have bounded
9244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * {@link #mCurrentWindowStart} to be non-negative
9344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
9444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mCurrentWindowStartUnbounded = 0;
9544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
9644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
9744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Indicates whether to treat the adapter to be a circular structure, ie.
9844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * the view before 0 is considered to be <code>mAdapter.getCount() - 1</code>
9944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
10044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * TODO: this doesn't do anything yet
10144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
10244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
10344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    boolean mCycleViews = false;
10444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
10544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
10644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Handler to post events to the main thread
10744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
10844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    Handler mMainQueue;
10944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
11044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
11144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Listens for data changes from the adapter
11244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
1133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    AdapterDataSetObserver mDataSetObserver;
1143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
11544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
11644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The {@link Adapter} for this {@link AdapterViewAnimator}
11744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
11844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    Adapter mAdapter;
11944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
12044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
12144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * The {@link RemoteViewsAdapter} for this {@link AdapterViewAnimator}
12244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
12344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    RemoteViewsAdapter mRemoteViewsAdapter;
12444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
12544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
12644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Specifies whether this is the first time the animator is showing views
12744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
12844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    boolean mFirstTime = true;
1293db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
13044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
13144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * TODO: Animation stuff is still in flux, waiting on the new framework to settle a bit.
13244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
1333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    Animation mInAnimation;
1343db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    Animation mOutAnimation;
13544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private  ArrayList<View> mViewsToBringToFront;
1363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public AdapterViewAnimator(Context context) {
1383db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        super(context);
1395b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy        initViewAnimator();
1403db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
1413db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1423db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public AdapterViewAnimator(Context context, AttributeSet attrs) {
1433db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        super(context, attrs);
1443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
14544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        TypedArray a = context.obtainStyledAttributes(attrs,
14644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                com.android.internal.R.styleable.ViewAnimator);
14744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int resource = a.getResourceId(
14844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                com.android.internal.R.styleable.ViewAnimator_inAnimation, 0);
1493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (resource > 0) {
1503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            setInAnimation(context, resource);
1513db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
1523db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1533db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_outAnimation, 0);
1543db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (resource > 0) {
1553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            setOutAnimation(context, resource);
1563db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
1573db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
15844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        boolean flag = a.getBoolean(
15944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                com.android.internal.R.styleable.ViewAnimator_animateFirstView, true);
1603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setAnimateFirstView(flag);
1613db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1623db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        a.recycle();
1633db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1645b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy        initViewAnimator();
1653db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
1663db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
1673db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
1683db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Initialize this {@link AdapterViewAnimator}
1693db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
1705b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy    private void initViewAnimator() {
1713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mMainQueue = new Handler(Looper.myLooper());
17244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mActiveViews = new View[mNumActiveViews];
17344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mPreviousViews = new ArrayList<View>();
17444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mViewsToBringToFront = new ArrayList<View>();
17544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
17644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
17744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
17844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * This method is used by subclasses to configure the animator to display the
17944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * desired number of views, and specify the offset
18044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
18144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param numVisibleViews The number of views the animator keeps in the {@link ViewGroup}
1825b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy     * @param activeOffset This parameter specifies where the current index ({@link #mWhichChild})
18344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        sits within the window. For example if activeOffset is 1, and numVisibleViews is 3,
1845b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy     *        and {@link #setDisplayedChild(int)} is called with 10, then the effective window will
1855b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy     *        be the indexes 9, 10, and 11. In the same example, if activeOffset were 0, then the
18644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        window would instead contain indexes 10, 11 and 12.
18744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
18844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     void configureViewAnimator(int numVisibleViews, int activeOffset) {
18944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (activeOffset > numVisibleViews - 1) {
19044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Throw an exception here.
19144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
19244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mNumActiveViews = numVisibleViews;
19344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mActiveOffset = activeOffset;
19444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mActiveViews = new View[mNumActiveViews];
19544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mPreviousViews.clear();
19644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        removeAllViewsInLayout();
19744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mCurrentWindowStart = 0;
19844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mCurrentWindowEnd = -1;
19944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
20044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
20144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
20244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * This class should be overridden by subclasses to customize view transitions within
20344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * the set of visible views
20444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
20544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param fromIndex The relative index within the window that the view was in, -1 if it wasn't
20644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        in the window
20744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param toIndex The relative index within the window that the view is going to, -1 if it is
20844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        being removed
20944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param view The view that is being animated
21044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
21144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    void animateViewForTransition(int fromIndex, int toIndex, View view) {
21244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        PropertyAnimator pa;
21344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (fromIndex == -1) {
21444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            pa = new PropertyAnimator(400, view, "alpha", 0.0f, 1.0f);
21544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            pa.start();
21644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        } else if (toIndex == -1) {
21744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            pa = new PropertyAnimator(400, view, "alpha", 1.0f, 0.0f);
21844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            pa.start();
21944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
2203db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
2213db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2223db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
2233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Sets which child view will be displayed.
2243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
2253db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param whichChild the index of the child view to display
2263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
2273db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setDisplayedChild(int whichChild) {
2283db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (mAdapter != null) {
2293db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            mWhichChild = whichChild;
2303db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            if (whichChild >= mAdapter.getCount()) {
2313db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                mWhichChild = 0;
2323db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            } else if (whichChild < 0) {
2333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                mWhichChild = mAdapter.getCount() - 1;
2343db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
2353db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            boolean hasFocus = getFocusedChild() != null;
2373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            // This will clear old focus if we had it
2383db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            showOnly(mWhichChild);
2393db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            if (hasFocus) {
2403db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                // Try to retake focus if we had it
2413db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                requestFocus(FOCUS_FORWARD);
2423db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
2433db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
2443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
2453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2463db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
2473db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Return default inAnimation. To be overriden by subclasses.
2483db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
24944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    Animation getDefaultInAnimation() {
2503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return null;
2513db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
2523db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2533db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
25444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Return default outAnimation. To be overridden by subclasses.
2553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
25644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    Animation getDefaultOutAnimation() {
2573db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return null;
2583db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
2593db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
26144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * To be overridden by subclasses. This method applies a view / index specific
26244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * transform to the child view.
26344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
26444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param child
26544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param relativeIndex
26644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
26744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    void applyTransformForChildAtIndex(View child, int relativeIndex) {
26844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
26944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
27044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
2713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Returns the index of the currently displayed child view.
2723db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
2733db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public int getDisplayedChild() {
2743db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return mWhichChild;
2753db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
2763db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2773db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
2783db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Manually shows the next child.
2793db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
2803db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void showNext() {
2813db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setDisplayedChild(mWhichChild + 1);
2823db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
2833db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2843db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
2853db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Manually shows the previous child.
2863db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
2873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void showPrevious() {
2883db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setDisplayedChild(mWhichChild - 1);
2893db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
2903db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
2913db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
2923db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Shows only the specified child. The other displays Views exit the screen,
2933db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * optionally with the with the {@link #getOutAnimation() out animation} and
2943db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * the specified child enters the screen, optionally with the
2953db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * {@link #getInAnimation() in animation}.
2963db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
2973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param childIndex The index of the child to be shown.
2983db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param animate Whether or not to use the in and out animations, defaults
2993db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *            to true.
3003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
3013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    void showOnly(int childIndex, boolean animate) {
3023db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        showOnly(childIndex, animate, false);
3033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
3043db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
30544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private int modulo(int pos, int size) {
30644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        return (size + (pos % size)) % size;
3073db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
3083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
30944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
31044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Get the view at this index relative to the current window's start
31144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
31244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param relativeIndex Position relative to the current window's start
31344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @return View at this index, null if the index is outside the bounds
31444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
31544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    View getViewAtRelativeIndex(int relativeIndex) {
31644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (relativeIndex >= 0 && relativeIndex <= mNumActiveViews - 1) {
31744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            int index = mCurrentWindowStartUnbounded + relativeIndex;
31844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            return mActiveViews[modulo(index, mNumActiveViews)];
31944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
32044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        return null;
32144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
3223db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
32344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private LayoutParams createOrReuseLayoutParams(View v) {
3245b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy        final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
32544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (currentLp instanceof LayoutParams) {
3265b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy            return (LayoutParams) currentLp;
32744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
32844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        return new LayoutParams(v);
32944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
3303db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
33144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    void showOnly(int childIndex, boolean animate, boolean onLayout) {
33244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (mAdapter == null) return;
3333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
33444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        for (int i = 0; i < mPreviousViews.size(); i++) {
33544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            View viewToRemove = mPreviousViews.get(i);
33644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            viewToRemove.clearAnimation();
33744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // applyTransformForChildAtIndex here just allows for any cleanup
33844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // associated with this view that may need to be done by a subclass
33944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            applyTransformForChildAtIndex(viewToRemove, -1);
34044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            removeViewInLayout(viewToRemove);
34144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
34244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mPreviousViews.clear();
34344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int newWindowStartUnbounded = childIndex - mActiveOffset;
34444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int newWindowEndUnbounded = newWindowStartUnbounded + mNumActiveViews - 1;
34544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int newWindowStart = Math.max(0, newWindowStartUnbounded);
34644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int newWindowEnd = Math.min(mAdapter.getCount(), newWindowEndUnbounded);
34744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
34844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // This section clears out any items that are in our mActiveViews list
34944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // but are outside the effective bounds of our window (this is becomes an issue
35044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // at the extremities of the list, eg. where newWindowStartUnbounded < 0 or
35144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // newWindowEndUnbounded > mAdapter.getCount() - 1
35244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        for (int i = newWindowStartUnbounded; i < newWindowEndUnbounded; i++) {
35344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            if (i < newWindowStart || i > newWindowEnd) {
35444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                int index = modulo(i, mNumActiveViews);
35544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                if (mActiveViews[index] != null) {
35644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    View previousView = mActiveViews[index];
35744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    mPreviousViews.add(previousView);
35844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    int previousViewRelativeIndex = modulo(index - mCurrentWindowStart,
35944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            mNumActiveViews);
36044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    animateViewForTransition(previousViewRelativeIndex, -1, previousView);
36144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                }
3623db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
36344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
3643db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
36544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // If the window has changed
36644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (! (newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd)) {
36744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Run through the indices in the new range
36844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            for (int i = newWindowStart; i <= newWindowEnd; i++) {
36944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
37044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                int oldRelativeIndex = i - mCurrentWindowStartUnbounded;
37144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                int newRelativeIndex = i - newWindowStartUnbounded;
37244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                int index = modulo(i, mNumActiveViews);
37344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
37444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // If this item is in the current window, great, we just need to apply
37544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // the transform for it's new relative position in the window, and animate
37644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // between it's current and new relative positions
37744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                if (i >= mCurrentWindowStart && i <= mCurrentWindowEnd) {
37844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    View view = mActiveViews[index];
37944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    applyTransformForChildAtIndex(view, newRelativeIndex);
38044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    animateViewForTransition(oldRelativeIndex, newRelativeIndex, view);
38144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
38244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // Otherwise this view is new, so first we have to displace the view that's
38344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // taking the new view's place within our cache (a circular array)
38444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                } else {
38544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    if (mActiveViews[index] != null) {
38644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        View previousView = mActiveViews[index];
38744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        mPreviousViews.add(previousView);
38844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        int previousViewRelativeIndex = modulo(index - mCurrentWindowStart,
38944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                                mNumActiveViews);
39044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        animateViewForTransition(previousViewRelativeIndex, -1, previousView);
39144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
39244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        if (mCurrentWindowStart > newWindowStart) {
39344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            mViewsToBringToFront.add(previousView);
39444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        }
39544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    }
3963db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
39744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    // We've cleared a spot for the new view. Get it from the adapter, add it
39844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    // and apply any transform / animation
39944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    View newView = mAdapter.getView(i, null, this);
40044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    if (newView != null) {
40144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        mActiveViews[index] = newView;
40244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        addViewInLayout(newView, -1, createOrReuseLayoutParams(newView));
40344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        applyTransformForChildAtIndex(newView, newRelativeIndex);
40444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        animateViewForTransition(-1, newRelativeIndex, newView);
40544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    }
4063db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                }
40744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                mActiveViews[index].bringToFront();
4083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
4093db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
41044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            for (int i = 0; i < mViewsToBringToFront.size(); i++) {
41144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                View v = mViewsToBringToFront.get(i);
41244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                v.bringToFront();
4133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            }
41444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mViewsToBringToFront.clear();
4153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
41644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mCurrentWindowStart = newWindowStart;
41744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mCurrentWindowEnd = newWindowEnd;
41844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mCurrentWindowStartUnbounded = newWindowStartUnbounded;
41944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
42044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
42144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mFirstTime = false;
42244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (!onLayout) {
42344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            requestLayout();
42444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            invalidate();
42544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        } else {
42644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // If the Adapter tries to layout the current view when we get it using getView
42744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // above the layout will end up being ignored since we are currently laying out, so
42844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // we post a delayed requestLayout and invalidate
42944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mMainQueue.post(new Runnable() {
43044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                @Override
43144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                public void run() {
43244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    requestLayout();
43344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    invalidate();
43444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                }
43544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            });
4363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
4373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
4383db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4393db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
4403db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
4413db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        boolean dataChanged = mDataChanged;
4423db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (dataChanged) {
4433db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            handleDataChanged();
4443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            // if the data changes, mWhichChild might be out of the bounds of the adapter
4463db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            // in this case, we reset mWhichChild to the beginning
4473db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            if (mWhichChild >= mAdapter.getCount())
4483db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                mWhichChild = 0;
4493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            showOnly(mWhichChild, true, true);
4513db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
4523db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4533db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        final int childCount = getChildCount();
4543db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        for (int i = 0; i < childCount; i++) {
4553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            final View child = getChildAt(i);
4563db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4573db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            int childRight = mPaddingLeft + child.getMeasuredWidth();
4583db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            int childBottom = mPaddingTop + child.getMeasuredHeight();
45944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            LayoutParams lp = (LayoutParams) child.getLayoutParams();
4603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
46144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset,
46244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    childRight + lp.horizontalOffset, childBottom + lp.verticalOffset);
4633db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
4643db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mDataChanged = false;
4653db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
4663db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4673db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
4683db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
4693db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        final int count = getChildCount();
4703db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
4723db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
4733db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4743db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        for (int i = 0; i < count; i++) {
4753db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            final View child = getChildAt(i);
4763db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4773db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4783db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4793db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            lp.width = widthSpecSize - mPaddingLeft - mPaddingRight;
4803db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            lp.height = heightSpecSize - mPaddingTop - mPaddingBottom;
4813db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4823db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width,
4833db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                    MeasureSpec.EXACTLY);
4843db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
4853db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen                    MeasureSpec.EXACTLY);
4863db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            child.measure(childWidthMeasureSpec, childheightMeasureSpec);
4883db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
4893db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setMeasuredDimension(widthSpecSize, heightSpecSize);
4903db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
4913db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
4923db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
4933db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Shows only the specified child. The other displays Views exit the screen
4943db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * with the {@link #getOutAnimation() out animation} and the specified child
4953db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * enters the screen with the {@link #getInAnimation() in animation}.
4963db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
4973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param childIndex The index of the child to be shown.
4983db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
4993db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    void showOnly(int childIndex) {
5003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        final boolean animate = (!mFirstTime || mAnimateFirstTime);
5013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        showOnly(childIndex, animate);
5023db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
5033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
5043db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
5053db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Returns the View corresponding to the currently displayed child.
5063db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5073db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @return The View currently displayed.
5083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5093db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getDisplayedChild()
5103db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
5113db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public View getCurrentView() {
51244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        return getViewAtRelativeIndex(mActiveOffset);
5133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
5143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
5153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
5163db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Returns the current animation used to animate a View that enters the screen.
5173db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5183db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @return An Animation or null if none is set.
5193db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5203db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setInAnimation(android.view.animation.Animation)
5213db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setInAnimation(android.content.Context, int)
5223db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
5233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public Animation getInAnimation() {
5243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return mInAnimation;
5253db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
5263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
5273db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
5283db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Specifies the animation used to animate a View that enters the screen.
5293db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5303db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param inAnimation The animation started when a View enters the screen.
5313db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5323db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getInAnimation()
5333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setInAnimation(android.content.Context, int)
5343db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
5353db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setInAnimation(Animation inAnimation) {
5363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mInAnimation = inAnimation;
5373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
5383db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
5393db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
5403db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Returns the current animation used to animate a View that exits the screen.
5413db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5423db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @return An Animation or null if none is set.
5433db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setOutAnimation(android.view.animation.Animation)
5453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setOutAnimation(android.content.Context, int)
5463db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
5473db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public Animation getOutAnimation() {
5483db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return mOutAnimation;
5493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
5503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
5513db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
5523db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Specifies the animation used to animate a View that exit the screen.
5533db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5543db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param outAnimation The animation started when a View exit the screen.
5553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5563db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getOutAnimation()
5573db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setOutAnimation(android.content.Context, int)
5583db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
5593db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setOutAnimation(Animation outAnimation) {
5603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mOutAnimation = outAnimation;
5613db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
5623db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
5633db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
5643db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Specifies the animation used to animate a View that enters the screen.
5653db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5663db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param context The application's environment.
5673db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param resourceID The resource id of the animation.
5683db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5693db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getInAnimation()
5703db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setInAnimation(android.view.animation.Animation)
5713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
5723db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setInAnimation(Context context, int resourceID) {
5733db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setInAnimation(AnimationUtils.loadAnimation(context, resourceID));
5743db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
5753db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
5763db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
5773db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Specifies the animation used to animate a View that exit the screen.
5783db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5793db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param context The application's environment.
5803db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param resourceID The resource id of the animation.
5813db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5823db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #getOutAnimation()
5833db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @see #setOutAnimation(android.view.animation.Animation)
5843db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
5853db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setOutAnimation(Context context, int resourceID) {
5863db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setOutAnimation(AnimationUtils.loadAnimation(context, resourceID));
5873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
5883db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
5893db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
5903db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Indicates whether the current View should be animated the first time
5913db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * the ViewAnimation is displayed.
5923db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *
5933db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * @param animate True to animate the current View the first time it is displayed,
5943db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     *                false otherwise.
5953db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
5963db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setAnimateFirstView(boolean animate) {
5973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mAnimateFirstTime = animate;
5983db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
5993db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
6013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public int getBaseline() {
6023db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline();
6033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
6043db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6053db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
6063db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public Adapter getAdapter() {
6073db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        return mAdapter;
6083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
6093db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6103db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
6113db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setAdapter(Adapter adapter) {
6128322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen        if (mAdapter != null && mDataSetObserver != null) {
6138322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen            mAdapter.unregisterDataSetObserver(mDataSetObserver);
6148322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen        }
6158322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen
6163db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mAdapter = adapter;
6173db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6183db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (mAdapter != null) {
6193db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            mDataSetObserver = new AdapterDataSetObserver();
6203db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            mAdapter.registerDataSetObserver(mDataSetObserver);
6213db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
62244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        setFocusable(true);
6233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
6243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6253db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
62644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Sets up this AdapterViewAnimator to use a remote views adapter which connects to a
62744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * RemoteViewsService through the specified intent.
62844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *
62944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * @param intent the intent used to identify the RemoteViewsService for the adapter to
63044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     *        connect to.
6313db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
6323db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @android.view.RemotableViewMethod
6333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setRemoteViewsAdapter(Intent intent) {
6343db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this);
6353db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
6363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
6383db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void setSelection(int position) {
6393db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        setDisplayedChild(position);
6403db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
6413db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6423db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    @Override
6433db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public View getSelectedView() {
64444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        return getViewAtRelativeIndex(mActiveOffset);
6453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
6463db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6473db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
6483db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Called back when the adapter connects to the RemoteViewsService.
6493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
6503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void onRemoteAdapterConnected() {
6513db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (mRemoteViewsAdapter != mAdapter) {
6523db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            setAdapter(mRemoteViewsAdapter);
6533db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
6543db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
6553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen
6563db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    /**
6573db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     * Called back when the adapter disconnects from the RemoteViewsService.
6583db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen     */
6593db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    public void onRemoteAdapterDisconnected() {
6603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        if (mRemoteViewsAdapter != mAdapter) {
6613db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            mRemoteViewsAdapter = null;
6623db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen            setAdapter(mRemoteViewsAdapter);
6633db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen        }
6643db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen    }
66544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
66644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    static class LayoutParams extends ViewGroup.LayoutParams {
66744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int horizontalOffset;
66844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int verticalOffset;
66944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        View mView;
67044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
67144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        LayoutParams(View view) {
67244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            super(0, 0);
67344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            horizontalOffset = 0;
67444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            verticalOffset = 0;
67544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mView = view;
67644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
67744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
67844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        LayoutParams(Context c, AttributeSet attrs) {
67944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            super(c, attrs);
68044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            horizontalOffset = 0;
68144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            verticalOffset = 0;
68244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
68344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
68444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        private Rect parentRect = new Rect();
68544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        void invalidateGlobalRegion(View v, Rect r) {
68644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            View p = v;
68744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            boolean firstPass = true;
68844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            parentRect.set(0, 0, 0, 0);
68944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            while (p.getParent() != null && p.getParent() instanceof View
69044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    && !parentRect.contains(r)) {
69144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                if (!firstPass) r.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY());
69244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                firstPass = false;
69344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                p = (View) p.getParent();
69444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                parentRect.set(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY(),
69544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        p.getRight() - p.getScrollX(), p.getBottom() - p.getScrollY());
69644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
69744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            p.invalidate(r.left, r.top, r.right, r.bottom);
69844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
69944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
70044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        private Rect invalidateRect = new Rect();
70144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // This is public so that PropertyAnimator can access it
70244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        public void setVerticalOffset(int newVerticalOffset) {
70344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            int offsetDelta = newVerticalOffset - verticalOffset;
70444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            verticalOffset = newVerticalOffset;
70544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            if (mView != null) {
70644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                mView.requestLayout();
70744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                int top = Math.min(mView.getTop() + offsetDelta, mView.getTop());
70844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                int bottom = Math.max(mView.getBottom() + offsetDelta, mView.getBottom());
70944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                invalidateRect.set(mView.getLeft(), top, mView.getRight(), bottom);
71044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                invalidateGlobalRegion(mView, invalidateRect);
71144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
71244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
71332a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen
71432a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen        public void setHorizontalOffset(int newHorizontalOffset) {
71532a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen            int offsetDelta = newHorizontalOffset - horizontalOffset;
71632a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen            horizontalOffset = newHorizontalOffset;
71732a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen            if (mView != null) {
71832a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen                mView.requestLayout();
71932a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen                int left = Math.min(mView.getLeft() + offsetDelta, mView.getLeft());
72032a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen                int right = Math.max(mView.getRight() + offsetDelta, mView.getRight());
72132a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen                invalidateRect.set(left, mView.getTop(), right, mView.getBottom());
72232a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen                invalidateGlobalRegion(mView, invalidateRect);
72332a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen            }
72432a42f1587db77b958d62c3de4f2734eb0a3b965Adam Cohen        }
72544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
7263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen}
727