AdapterViewAnimator.java revision a9238c89a43500ed0bcdeaee182be08ff991c627
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; 201b065cd1401253f999caa5d0ac12909407cef00eAdam Cohenimport java.util.HashMap; 2144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 22ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohenimport android.animation.AnimatorInflater; 23a18a86b43e40e3c15dcca0ae0148d641be9b25feChet Haaseimport android.animation.ObjectAnimator; 243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.content.Context; 253db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.content.Intent; 263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.content.res.TypedArray; 273db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.os.Handler; 283db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.os.Looper; 29b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohenimport android.os.Parcel; 30b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohenimport android.os.Parcelable; 313db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.util.AttributeSet; 323db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.view.View; 3344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.ViewGroup; 34839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohenimport android.view.ViewGroup.LayoutParams; 353db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.view.animation.Animation; 363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohenimport android.view.animation.AnimationUtils; 373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 383db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen/** 393db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Base class for a {@link AdapterView} that will perform animations 403db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * when switching between its views. 413db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 423db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_inAnimation 433db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_outAnimation 443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_animateFirstView 451b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen * @attr ref android.R.styleable#AdapterViewAnimator_loopViews 463db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 4744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenpublic abstract class AdapterViewAnimator extends AdapterView<Adapter> 48dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen implements RemoteViewsAdapter.RemoteAdapterConnectionCallback { 493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen private static final String TAG = "RemoteViewAnimator"; 503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 5144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 5244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * The index of the current child, which appears anywhere from the beginning 5344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * to the end of the current set of children, as specified by {@link #mActiveOffset} 5444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen int mWhichChild = 0; 5644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 5744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 5844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Whether or not the first view(s) should be animated in 5944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen boolean mAnimateFirstTime = true; 613db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 6244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 6344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Represents where the in the current window of 6444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * views the current <code>mDisplayedChild</code> sits 6544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 6644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int mActiveOffset = 0; 6744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 6844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 6944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * The number of views that the {@link AdapterViewAnimator} keeps as children at any 7044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * given time (not counting views that are pending removal, see {@link #mPreviousViews}). 7144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 7296d8d56302da81b24333b204e6d7f15064538036Adam Cohen int mMaxNumActiveViews = 1; 7344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 7444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 751b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen * Map of the children of the {@link AdapterViewAnimator}. 7644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 7796d8d56302da81b24333b204e6d7f15064538036Adam Cohen HashMap<Integer, ViewAndIndex> mViewsMap = new HashMap<Integer, ViewAndIndex>(); 7844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 7944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 8044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * List of views pending removal from the {@link AdapterViewAnimator} 8144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 821b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen ArrayList<Integer> mPreviousViews; 8344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 8444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 8544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * The index, relative to the adapter, of the beginning of the window of views 8644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 8744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int mCurrentWindowStart = 0; 8844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 8944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 9044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * The index, relative to the adapter, of the end of the window of views 9144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 9244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int mCurrentWindowEnd = -1; 9344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 9444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 9544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * The same as {@link #mCurrentWindowStart}, except when the we have bounded 9644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * {@link #mCurrentWindowStart} to be non-negative 9744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 9844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int mCurrentWindowStartUnbounded = 0; 9944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 10044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 10144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Handler to post events to the main thread 10244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 10344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen Handler mMainQueue; 10444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 10544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 10644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Listens for data changes from the adapter 10744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 1083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen AdapterDataSetObserver mDataSetObserver; 1093db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 11044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 11144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * The {@link Adapter} for this {@link AdapterViewAnimator} 11244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 11344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen Adapter mAdapter; 11444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 11544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 11644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * The {@link RemoteViewsAdapter} for this {@link AdapterViewAnimator} 11744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 11844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen RemoteViewsAdapter mRemoteViewsAdapter; 11944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 12044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 12144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Specifies whether this is the first time the animator is showing views 12244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 12344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen boolean mFirstTime = true; 1243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 12544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 126b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen * Specifies if the animator should wrap from 0 to the end and vice versa 127b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen * or have hard boundaries at the beginning and end 128b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen */ 1291b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen boolean mLoopViews = true; 130b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 131b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen /** 132839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen * The width and height of some child, used as a size reference in-case our 133839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen * dimensions are unspecified by the parent. 134839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen */ 135839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen int mReferenceChildWidth = -1; 136839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen int mReferenceChildHeight = -1; 137839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 138839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen /** 139ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen * In and out animations. 14044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 1412794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase ObjectAnimator mInAnimation; 1422794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase ObjectAnimator mOutAnimation; 143ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen 144ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen private static final int DEFAULT_ANIMATION_DURATION = 200; 145ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen 1463db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public AdapterViewAnimator(Context context) { 1473db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen super(context); 1485b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy initViewAnimator(); 1493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 1503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 1513db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public AdapterViewAnimator(Context context, AttributeSet attrs) { 1523db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen super(context, attrs); 1533db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 15444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen TypedArray a = context.obtainStyledAttributes(attrs, 1551b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen com.android.internal.R.styleable.AdapterViewAnimator); 15644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int resource = a.getResourceId( 1571b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen com.android.internal.R.styleable.AdapterViewAnimator_inAnimation, 0); 1583db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen if (resource > 0) { 1593db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen setInAnimation(context, resource); 160ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen } else { 161ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen setInAnimation(getDefaultInAnimation()); 1623db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 1633db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 1641b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen resource = a.getResourceId(com.android.internal.R.styleable.AdapterViewAnimator_outAnimation, 0); 1653db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen if (resource > 0) { 1663db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen setOutAnimation(context, resource); 167ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen } else { 168ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen setOutAnimation(getDefaultOutAnimation()); 1693db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 1703db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 17144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen boolean flag = a.getBoolean( 1721b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen com.android.internal.R.styleable.AdapterViewAnimator_animateFirstView, true); 1733db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen setAnimateFirstView(flag); 1743db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 1751b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen mLoopViews = a.getBoolean( 1761b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen com.android.internal.R.styleable.AdapterViewAnimator_loopViews, false); 1771b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen 1783db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen a.recycle(); 1793db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 1805b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy initViewAnimator(); 1813db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 1823db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 1833db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 1843db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Initialize this {@link AdapterViewAnimator} 1853db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 1865b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy private void initViewAnimator() { 1873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen mMainQueue = new Handler(Looper.myLooper()); 1881b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen mPreviousViews = new ArrayList<Integer>(); 18944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 19044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 19196d8d56302da81b24333b204e6d7f15064538036Adam Cohen class ViewAndIndex { 1921b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen ViewAndIndex(View v, int i) { 1931b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen view = v; 1941b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen index = i; 1951b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen } 1961b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen View view; 1971b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen int index; 1981b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen } 1991b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen 20044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 20144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * This method is used by subclasses to configure the animator to display the 20244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * desired number of views, and specify the offset 20344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * 20444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * @param numVisibleViews The number of views the animator keeps in the {@link ViewGroup} 2055b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy * @param activeOffset This parameter specifies where the current index ({@link #mWhichChild}) 20644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * sits within the window. For example if activeOffset is 1, and numVisibleViews is 3, 2075b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy * and {@link #setDisplayedChild(int)} is called with 10, then the effective window will 2085b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy * be the indexes 9, 10, and 11. In the same example, if activeOffset were 0, then the 20944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * window would instead contain indexes 10, 11 and 12. 210b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen * @param shouldLoop If the animator is show view 0, and setPrevious() is called, do we 211b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen * we loop back to the end, or do we do nothing 21244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 2131b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen void configureViewAnimator(int numVisibleViews, int activeOffset) { 21444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (activeOffset > numVisibleViews - 1) { 21544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // Throw an exception here. 21644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 21796d8d56302da81b24333b204e6d7f15064538036Adam Cohen mMaxNumActiveViews = numVisibleViews; 21844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mActiveOffset = activeOffset; 21944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mPreviousViews.clear(); 2201b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen mViewsMap.clear(); 22144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen removeAllViewsInLayout(); 22244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mCurrentWindowStart = 0; 22344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mCurrentWindowEnd = -1; 22444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 22544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 22644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 22744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * This class should be overridden by subclasses to customize view transitions within 22844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * the set of visible views 22944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * 23044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * @param fromIndex The relative index within the window that the view was in, -1 if it wasn't 23144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * in the window 23244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * @param toIndex The relative index within the window that the view is going to, -1 if it is 23344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * being removed 23444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * @param view The view that is being animated 23544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 23644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen void animateViewForTransition(int fromIndex, int toIndex, View view) { 23744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (fromIndex == -1) { 238ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen mInAnimation.setTarget(view); 239ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen mInAnimation.start(); 24044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } else if (toIndex == -1) { 241ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen mOutAnimation.setTarget(view); 242ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen mOutAnimation.start(); 24344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 2443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 2453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 2462794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase ObjectAnimator getDefaultInAnimation() { 2472794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase ObjectAnimator anim = ObjectAnimator.ofFloat(null, "alpha", 0.0f, 1.0f); 2482794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase anim.setDuration(DEFAULT_ANIMATION_DURATION); 2492794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase return anim; 250ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen } 251ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen 2522794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase ObjectAnimator getDefaultOutAnimation() { 2532794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase ObjectAnimator anim = ObjectAnimator.ofFloat(null, "alpha", 1.0f, 0.0f); 2542794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase anim.setDuration(DEFAULT_ANIMATION_DURATION); 2552794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase return anim; 256ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen } 257ef52176f1244a5bb98d82a0c8c7f4351edec17a1Adam Cohen 2583db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 2593db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Sets which child view will be displayed. 2603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 2613db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @param whichChild the index of the child view to display 2623db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 2633db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public void setDisplayedChild(int whichChild) { 2643db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen if (mAdapter != null) { 2653db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen mWhichChild = whichChild; 26696d8d56302da81b24333b204e6d7f15064538036Adam Cohen if (whichChild >= getWindowSize()) { 26796d8d56302da81b24333b204e6d7f15064538036Adam Cohen mWhichChild = mLoopViews ? 0 : getWindowSize() - 1; 2683db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } else if (whichChild < 0) { 26996d8d56302da81b24333b204e6d7f15064538036Adam Cohen mWhichChild = mLoopViews ? getWindowSize() - 1 : 0; 2703db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 2713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 2723db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen boolean hasFocus = getFocusedChild() != null; 2733db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen // This will clear old focus if we had it 2743db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen showOnly(mWhichChild); 2753db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen if (hasFocus) { 2763db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen // Try to retake focus if we had it 2773db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen requestFocus(FOCUS_FORWARD); 2783db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 2793db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 2803db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 2813db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 2823db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 28344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * To be overridden by subclasses. This method applies a view / index specific 28444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * transform to the child view. 28544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * 28644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * @param child 28744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * @param relativeIndex 28844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 28944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen void applyTransformForChildAtIndex(View child, int relativeIndex) { 29044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 29144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 29244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 2933db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Returns the index of the currently displayed child view. 2943db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 2953db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public int getDisplayedChild() { 2963db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen return mWhichChild; 2973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 2983db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 2993db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 3003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Manually shows the next child. 3013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 3023db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public void showNext() { 3033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen setDisplayedChild(mWhichChild + 1); 3043db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 3053db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 3063db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 3073db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Manually shows the previous child. 3083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 3093db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public void showPrevious() { 3103db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen setDisplayedChild(mWhichChild - 1); 3113db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 3123db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 3133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 3143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Shows only the specified child. The other displays Views exit the screen, 3153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * optionally with the with the {@link #getOutAnimation() out animation} and 3163db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * the specified child enters the screen, optionally with the 3173db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * {@link #getInAnimation() in animation}. 3183db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 3193db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @param childIndex The index of the child to be shown. 3203db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @param animate Whether or not to use the in and out animations, defaults 3213db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * to true. 3223db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 3233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen void showOnly(int childIndex, boolean animate) { 3243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen showOnly(childIndex, animate, false); 3253db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 3263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 32796d8d56302da81b24333b204e6d7f15064538036Adam Cohen int modulo(int pos, int size) { 3283042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen if (size > 0) { 3293042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen return (size + (pos % size)) % size; 3303042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen } else { 3313042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen return 0; 3323042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen } 3333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 3343db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 33544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen /** 33644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Get the view at this index relative to the current window's start 33744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * 33844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * @param relativeIndex Position relative to the current window's start 33944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * @return View at this index, null if the index is outside the bounds 34044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */ 34144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen View getViewAtRelativeIndex(int relativeIndex) { 34296d8d56302da81b24333b204e6d7f15064538036Adam Cohen if (relativeIndex >= 0 && relativeIndex <= getNumActiveViews() - 1 && mAdapter != null) { 34396d8d56302da81b24333b204e6d7f15064538036Adam Cohen int i = modulo(mCurrentWindowStartUnbounded + relativeIndex, getWindowSize()); 3446f279627cfa3286e6901a8dc2ed8361576ce226dAdam Cohen if (mViewsMap.get(i) != null) { 3456f279627cfa3286e6901a8dc2ed8361576ce226dAdam Cohen return mViewsMap.get(i).view; 3466f279627cfa3286e6901a8dc2ed8361576ce226dAdam Cohen } 34744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 34844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen return null; 34944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 3503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 35196d8d56302da81b24333b204e6d7f15064538036Adam Cohen int getNumActiveViews() { 35296d8d56302da81b24333b204e6d7f15064538036Adam Cohen if (mAdapter != null) { 35396d8d56302da81b24333b204e6d7f15064538036Adam Cohen return Math.min(mAdapter.getCount() + 1, mMaxNumActiveViews); 35496d8d56302da81b24333b204e6d7f15064538036Adam Cohen } else { 35596d8d56302da81b24333b204e6d7f15064538036Adam Cohen return mMaxNumActiveViews; 35696d8d56302da81b24333b204e6d7f15064538036Adam Cohen } 35796d8d56302da81b24333b204e6d7f15064538036Adam Cohen } 35896d8d56302da81b24333b204e6d7f15064538036Adam Cohen 35996d8d56302da81b24333b204e6d7f15064538036Adam Cohen int getWindowSize() { 36096d8d56302da81b24333b204e6d7f15064538036Adam Cohen if (mAdapter != null) { 36196d8d56302da81b24333b204e6d7f15064538036Adam Cohen int adapterCount = mAdapter.getCount(); 36296d8d56302da81b24333b204e6d7f15064538036Adam Cohen if (adapterCount <= getNumActiveViews() && mLoopViews) { 36396d8d56302da81b24333b204e6d7f15064538036Adam Cohen return adapterCount*mMaxNumActiveViews; 36496d8d56302da81b24333b204e6d7f15064538036Adam Cohen } else { 36596d8d56302da81b24333b204e6d7f15064538036Adam Cohen return adapterCount; 36696d8d56302da81b24333b204e6d7f15064538036Adam Cohen } 36796d8d56302da81b24333b204e6d7f15064538036Adam Cohen } else { 36896d8d56302da81b24333b204e6d7f15064538036Adam Cohen return 0; 36996d8d56302da81b24333b204e6d7f15064538036Adam Cohen } 37096d8d56302da81b24333b204e6d7f15064538036Adam Cohen } 37196d8d56302da81b24333b204e6d7f15064538036Adam Cohen 3729b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen LayoutParams createOrReuseLayoutParams(View v) { 3735b53f9186e7812c93bc578d18e92cb123481fcbcRomain Guy final ViewGroup.LayoutParams currentLp = v.getLayoutParams(); 3749b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen if (currentLp instanceof ViewGroup.LayoutParams) { 375b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen LayoutParams lp = (LayoutParams) currentLp; 376b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen return lp; 37744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 3789b073948cfb84c0dd04f8a94ee1f7f263f027c83Adam Cohen return new ViewGroup.LayoutParams(0, 0); 37944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 3803db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 3816364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung void refreshChildren() { 382a9238c89a43500ed0bcdeaee182be08ff991c627Adam Cohen if (mAdapter == null) return; 383bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen for (int i = mCurrentWindowStart; i <= mCurrentWindowEnd; i++) { 384a9238c89a43500ed0bcdeaee182be08ff991c627Adam Cohen int index = modulo(i, getWindowSize()); 385bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen 386a9238c89a43500ed0bcdeaee182be08ff991c627Adam Cohen int adapterCount = mAdapter.getCount(); 387bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen // get the fresh child from the adapter 388a9238c89a43500ed0bcdeaee182be08ff991c627Adam Cohen View updatedChild = mAdapter.getView(modulo(i, adapterCount), null, this); 389bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen 3901b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen if (mViewsMap.containsKey(index)) { 3911b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen FrameLayout fl = (FrameLayout) mViewsMap.get(index).view; 392bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen // flush out the old child 393bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen fl.removeAllViewsInLayout(); 394bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen // add the new child to the frame, if it exists 395bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen if (updatedChild != null) { 396bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen fl.addView(updatedChild); 397bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen } 398bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen } 399bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen } 400bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen } 401bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen 402dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen /** 403dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen * This method can be overridden so that subclasses can provide a custom frame in which their 404dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen * children can live. For example, StackView adds padding to its childrens' frames so as to 405dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen * accomodate for the highlight effect. 406dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen * 407dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen * @return The FrameLayout into which children can be placed. 408dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen */ 409dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen FrameLayout getFrameForChild() { 410dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen return new FrameLayout(mContext); 411dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen } 412dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen 41344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen void showOnly(int childIndex, boolean animate, boolean onLayout) { 41444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (mAdapter == null) return; 4153042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen final int adapterCount = mAdapter.getCount(); 4163042944c6ec68210ba1746540b53789e70d15ef4Adam Cohen if (adapterCount == 0) return; 4173db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 41844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen for (int i = 0; i < mPreviousViews.size(); i++) { 4191b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen View viewToRemove = mViewsMap.get(mPreviousViews.get(i)).view; 4201b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen mViewsMap.remove(mPreviousViews.get(i)); 42144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen viewToRemove.clearAnimation(); 4223d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen if (viewToRemove instanceof ViewGroup) { 4233d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen ViewGroup vg = (ViewGroup) viewToRemove; 4243d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen vg.removeAllViewsInLayout(); 4253d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen } 42644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // applyTransformForChildAtIndex here just allows for any cleanup 42744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // associated with this view that may need to be done by a subclass 42844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen applyTransformForChildAtIndex(viewToRemove, -1); 4293d07af03421f4727ef7e97c5c19e6ade50b19060Adam Cohen 43044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen removeViewInLayout(viewToRemove); 43144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 43244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mPreviousViews.clear(); 43344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int newWindowStartUnbounded = childIndex - mActiveOffset; 43496d8d56302da81b24333b204e6d7f15064538036Adam Cohen int newWindowEndUnbounded = newWindowStartUnbounded + getNumActiveViews() - 1; 43544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int newWindowStart = Math.max(0, newWindowStartUnbounded); 4361b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen int newWindowEnd = Math.min(adapterCount - 1, newWindowEndUnbounded); 43744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 4381b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen if (mLoopViews) { 4391b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen newWindowStart = newWindowStartUnbounded; 4401b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen newWindowEnd = newWindowEndUnbounded; 4411b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen } 44296d8d56302da81b24333b204e6d7f15064538036Adam Cohen int rangeStart = modulo(newWindowStart, getWindowSize()); 44396d8d56302da81b24333b204e6d7f15064538036Adam Cohen int rangeEnd = modulo(newWindowEnd, getWindowSize()); 4441b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen 4451b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen boolean wrap = false; 4461b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen if (rangeStart > rangeEnd) { 4471b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen wrap = true; 4481b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen } 4491b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen 4501b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen // This section clears out any items that are in our active views list 45144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // but are outside the effective bounds of our window (this is becomes an issue 45244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // at the extremities of the list, eg. where newWindowStartUnbounded < 0 or 45344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // newWindowEndUnbounded > mAdapter.getCount() - 1 4541b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen for (Integer index : mViewsMap.keySet()) { 4551b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen boolean remove = false; 4561b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen if (!wrap && (index < rangeStart || index > rangeEnd)) { 4571b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen remove = true; 4581b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen } else if (wrap && (index > rangeEnd && index < rangeStart)) { 4591b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen remove = true; 4601b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen } 4611b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen 4621b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen if (remove) { 4631b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen View previousView = mViewsMap.get(index).view; 4641b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen int oldRelativeIndex = mViewsMap.get(index).index; 4651b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen 4661b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen mPreviousViews.add(index); 4671b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen animateViewForTransition(oldRelativeIndex, -1, previousView); 4683db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 46944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 4703db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 47144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // If the window has changed 47296d8d56302da81b24333b204e6d7f15064538036Adam Cohen if (!(newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd && 47396d8d56302da81b24333b204e6d7f15064538036Adam Cohen newWindowStartUnbounded == mCurrentWindowStartUnbounded)) { 47444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // Run through the indices in the new range 47544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen for (int i = newWindowStart; i <= newWindowEnd; i++) { 47644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 47796d8d56302da81b24333b204e6d7f15064538036Adam Cohen int index = modulo(i, getWindowSize()); 4781b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen int oldRelativeIndex; 4791b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen if (mViewsMap.containsKey(index)) { 4801b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen oldRelativeIndex = mViewsMap.get(index).index; 4811b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen } else { 4821b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen oldRelativeIndex = -1; 4831b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen } 48444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen int newRelativeIndex = i - newWindowStartUnbounded; 48544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 48644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // If this item is in the current window, great, we just need to apply 48744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // the transform for it's new relative position in the window, and animate 48844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // between it's current and new relative positions 4891b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen boolean inOldRange = mViewsMap.containsKey(index) && !mPreviousViews.contains(index); 4901b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen 4911b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen if (inOldRange) { 4921b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen View view = mViewsMap.get(index).view; 4931b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen mViewsMap.get(index).index = newRelativeIndex; 49444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen applyTransformForChildAtIndex(view, newRelativeIndex); 49544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen animateViewForTransition(oldRelativeIndex, newRelativeIndex, view); 49644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 4971b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen // Otherwise this view is new to the window 49844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } else { 4991b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen // Get the new view from the adapter, add it and apply any transform / animation 5001b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen View newView = mAdapter.getView(modulo(i, adapterCount), null, this); 501bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen 502bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen // We wrap the new view in a FrameLayout so as to respect the contract 503bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen // with the adapter, that is, that we don't modify this view directly 504dfcdddd7c408dddb22fb0867e4799d4c29d2f55fAdam Cohen FrameLayout fl = getFrameForChild(); 505bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen 506bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen // If the view from the adapter is null, we still keep an empty frame in place 50744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (newView != null) { 508bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen fl.addView(newView); 50944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 5101b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen mViewsMap.put(index, new ViewAndIndex(fl, newRelativeIndex)); 511bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen addChild(fl); 512bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen applyTransformForChildAtIndex(fl, newRelativeIndex); 513bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen animateViewForTransition(-1, newRelativeIndex, fl); 5143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 5151b065cd1401253f999caa5d0ac12909407cef00eAdam Cohen mViewsMap.get(index).view.bringToFront(); 5163db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 51744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mCurrentWindowStart = newWindowStart; 51844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mCurrentWindowEnd = newWindowEnd; 51944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mCurrentWindowStartUnbounded = newWindowStartUnbounded; 52044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 52144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen 52244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mFirstTime = false; 52344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen if (!onLayout) { 52444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen requestLayout(); 52544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen invalidate(); 52644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } else { 52744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // If the Adapter tries to layout the current view when we get it using getView 52844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // above the layout will end up being ignored since we are currently laying out, so 52944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen // we post a delayed requestLayout and invalidate 53044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen mMainQueue.post(new Runnable() { 53144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen @Override 53244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen public void run() { 53344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen requestLayout(); 53444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen invalidate(); 53544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen } 53644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen }); 5373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 5383db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 5393db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 540839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen private void addChild(View child) { 541839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen addViewInLayout(child, -1, createOrReuseLayoutParams(child)); 542839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 543839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen // This code is used to obtain a reference width and height of a child in case we need 544839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen // to decide our own size. TODO: Do we want to update the size of the child that we're 545839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen // using for reference size? If so, when? 546839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (mReferenceChildWidth == -1 || mReferenceChildHeight == -1) { 547839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen int measureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 548839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen child.measure(measureSpec, measureSpec); 549839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen mReferenceChildWidth = child.getMeasuredWidth(); 550839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen mReferenceChildHeight = child.getMeasuredHeight(); 551839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 552839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 553839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 554839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen private void measureChildren() { 555839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen final int count = getChildCount(); 556839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight; 557839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen final int childHeight = mMeasuredHeight - mPaddingTop - mPaddingBottom; 558839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 559839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen for (int i = 0; i < count; i++) { 560839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen final View child = getChildAt(i); 561839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY), 562839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY)); 563839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 564839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 565839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 566839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen @Override 567839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 568839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); 569839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); 570839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); 571839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); 572839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 573839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen boolean haveChildRefSize = (mReferenceChildWidth != -1 && mReferenceChildHeight != -1); 574839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 575839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen // We need to deal with the case where our parent hasn't told us how 576839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen // big we should be. In this case we try to use the desired size of the first 577839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen // child added. 578839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (heightSpecMode == MeasureSpec.UNSPECIFIED) { 579839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen heightSpecSize = haveChildRefSize ? mReferenceChildHeight + mPaddingTop + 580839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen mPaddingBottom : 0; 581839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } else if (heightSpecMode == MeasureSpec.AT_MOST) { 582839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen heightSpecSize = haveChildRefSize ? Math.min(mReferenceChildHeight + mPaddingTop + 583839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen mPaddingBottom, heightSpecSize) : 0; 584839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 585839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 586839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen if (widthSpecMode == MeasureSpec.UNSPECIFIED) { 587839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft + 588839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen mPaddingRight : 0; 589839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } else if (heightSpecMode == MeasureSpec.AT_MOST) { 590839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen widthSpecSize = haveChildRefSize ? Math.min(mReferenceChildWidth + mPaddingLeft + 591839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen mPaddingRight, widthSpecSize) : 0; 592839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 593839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 594839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen setMeasuredDimension(widthSpecSize, heightSpecSize); 595839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen measureChildren(); 596839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen } 597839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen 5983db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen @Override 5993db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 6003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen boolean dataChanged = mDataChanged; 6013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen if (dataChanged) { 6023db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen handleDataChanged(); 6033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 6043db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen // if the data changes, mWhichChild might be out of the bounds of the adapter 6053db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen // in this case, we reset mWhichChild to the beginning 606bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen if (mWhichChild >= mAdapter.getCount()) { 6073db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen mWhichChild = 0; 6083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 609bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen showOnly(mWhichChild, true, true); 610bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen } 611bd0136a2fde1d81a835f94efe3193569b10d99ffAdam Cohen refreshChildren(); 6123db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 6133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 6143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen final int childCount = getChildCount(); 6153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen for (int i = 0; i < childCount; i++) { 6163db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen final View child = getChildAt(i); 6173db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 6183db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen int childRight = mPaddingLeft + child.getMeasuredWidth(); 6193db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen int childBottom = mPaddingTop + child.getMeasuredHeight(); 6203db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 621839f4a54e5a6fe602dbc5998b01412d809eba722Adam Cohen child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom); 6223db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 6233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen mDataChanged = false; 6243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 6253db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 626b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen static class SavedState extends BaseSavedState { 627b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen int whichChild; 628b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 629b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen /** 630b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen * Constructor called from {@link AdapterViewAnimator#onSaveInstanceState()} 631b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen */ 632b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen SavedState(Parcelable superState, int whichChild) { 633b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen super(superState); 634b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen this.whichChild = whichChild; 635b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 636b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 637b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen /** 638b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen * Constructor called from {@link #CREATOR} 639b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen */ 640b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen private SavedState(Parcel in) { 641b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen super(in); 6423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung this.whichChild = in.readInt(); 643b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 644b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 645b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen @Override 646b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen public void writeToParcel(Parcel out, int flags) { 647b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen super.writeToParcel(out, flags); 6483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung out.writeInt(this.whichChild); 649b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 650b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 651b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen @Override 652b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen public String toString() { 6533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return "AdapterViewAnimator.SavedState{ whichChild = " + this.whichChild + " }"; 654b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 655b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 656b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen public static final Parcelable.Creator<SavedState> CREATOR 657b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen = new Parcelable.Creator<SavedState>() { 658b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen public SavedState createFromParcel(Parcel in) { 659b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen return new SavedState(in); 660b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 661b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 662b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen public SavedState[] newArray(int size) { 663b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen return new SavedState[size]; 664b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 665b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen }; 666b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 667b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 668b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen @Override 669b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen public Parcelable onSaveInstanceState() { 670b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen Parcelable superState = super.onSaveInstanceState(); 671b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen return new SavedState(superState, mWhichChild); 672b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 673b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 674b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen @Override 675b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen public void onRestoreInstanceState(Parcelable state) { 676b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen SavedState ss = (SavedState) state; 677b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen super.onRestoreInstanceState(ss.getSuperState()); 678b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 679b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen // Here we set mWhichChild in addition to setDisplayedChild 680b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen // We do the former in case mAdapter is null, and hence setDisplayedChild won't 681b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen // set mWhichChild 682b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen mWhichChild = ss.whichChild; 683b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen setDisplayedChild(mWhichChild); 684b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen } 685b04f7ad90b7d5d5e0998e3b56960004cf56e6e8fAdam Cohen 6863db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 6873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Shows only the specified child. The other displays Views exit the screen 6883db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * with the {@link #getOutAnimation() out animation} and the specified child 6893db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * enters the screen with the {@link #getInAnimation() in animation}. 6903db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 6913db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @param childIndex The index of the child to be shown. 6923db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 6933db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen void showOnly(int childIndex) { 6943db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen final boolean animate = (!mFirstTime || mAnimateFirstTime); 6953db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen showOnly(childIndex, animate); 6963db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 6973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 6983db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 6993db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Returns the View corresponding to the currently displayed child. 7003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @return The View currently displayed. 7023db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @see #getDisplayedChild() 7043db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 7053db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public View getCurrentView() { 70644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen return getViewAtRelativeIndex(mActiveOffset); 7073db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 7083db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 7093db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 7103db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Returns the current animation used to animate a View that enters the screen. 7113db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7123db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @return An Animation or null if none is set. 7133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7142794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase * @see #setInAnimation(android.animation.ObjectAnimator) 7153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @see #setInAnimation(android.content.Context, int) 7163db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 7172794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase public ObjectAnimator getInAnimation() { 7183db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen return mInAnimation; 7193db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 7203db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 7213db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 7223db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Specifies the animation used to animate a View that enters the screen. 7233db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7243db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @param inAnimation The animation started when a View enters the screen. 7253db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @see #getInAnimation() 7273db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @see #setInAnimation(android.content.Context, int) 7283db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 7292794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase public void setInAnimation(ObjectAnimator inAnimation) { 7303db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen mInAnimation = inAnimation; 7313db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 7323db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 7333db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 7343db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Returns the current animation used to animate a View that exits the screen. 7353db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7363db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @return An Animation or null if none is set. 7373db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7382794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase * @see #setOutAnimation(android.animation.ObjectAnimator) 7393db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @see #setOutAnimation(android.content.Context, int) 7403db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 7412794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase public ObjectAnimator getOutAnimation() { 7423db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen return mOutAnimation; 7433db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 7443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 7453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 7463db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Specifies the animation used to animate a View that exit the screen. 7473db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7483db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @param outAnimation The animation started when a View exit the screen. 7493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @see #getOutAnimation() 7513db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @see #setOutAnimation(android.content.Context, int) 7523db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 7532794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase public void setOutAnimation(ObjectAnimator outAnimation) { 7543db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen mOutAnimation = outAnimation; 7553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 7563db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 7573db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 7583db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Specifies the animation used to animate a View that enters the screen. 7593db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @param context The application's environment. 7613db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @param resourceID The resource id of the animation. 7623db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7633db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @see #getInAnimation() 7642794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase * @see #setInAnimation(android.animation.ObjectAnimator) 7653db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 7663db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public void setInAnimation(Context context, int resourceID) { 7672794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase setInAnimation((ObjectAnimator) AnimatorInflater.loadAnimator(context, resourceID)); 7683db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 7693db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 7703db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 7713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Specifies the animation used to animate a View that exit the screen. 7723db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7733db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @param context The application's environment. 7743db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @param resourceID The resource id of the animation. 7753db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7763db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @see #getOutAnimation() 7772794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase * @see #setOutAnimation(android.animation.ObjectAnimator) 7783db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 7793db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public void setOutAnimation(Context context, int resourceID) { 7802794eb3b02e2404d453d3ad22a8a85a138130a07Chet Haase setOutAnimation((ObjectAnimator) AnimatorInflater.loadAnimator(context, resourceID)); 7813db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 7823db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 7833db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 7843db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Indicates whether the current View should be animated the first time 7853db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * the ViewAnimation is displayed. 7863db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * 7873db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * @param animate True to animate the current View the first time it is displayed, 7883db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * false otherwise. 7893db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 7903db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public void setAnimateFirstView(boolean animate) { 7913db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen mAnimateFirstTime = animate; 7923db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 7933db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 7943db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen @Override 7953db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public int getBaseline() { 7963db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline(); 7973db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 7983db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 7993db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen @Override 8003db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public Adapter getAdapter() { 8013db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen return mAdapter; 8023db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 8033db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 8043db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen @Override 8053db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public void setAdapter(Adapter adapter) { 8068322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen if (mAdapter != null && mDataSetObserver != null) { 8078322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen mAdapter.unregisterDataSetObserver(mDataSetObserver); 8088322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen } 8098322834a2544a673a35c5d4ad0d5909b3ca37600Adam Cohen 8103db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen mAdapter = adapter; 8111480fddea874a42adb43b4bcdac6704e4c3e110bAdam Cohen checkFocus(); 8123db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 8133db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen if (mAdapter != null) { 8143db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen mDataSetObserver = new AdapterDataSetObserver(); 8153db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen mAdapter.registerDataSetObserver(mDataSetObserver); 8163db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 81744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen setFocusable(true); 8183db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 8193db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 8203db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 82144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Sets up this AdapterViewAnimator to use a remote views adapter which connects to a 82244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * RemoteViewsService through the specified intent. 82344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * 82444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * @param intent the intent used to identify the RemoteViewsService for the adapter to 82544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * connect to. 8263db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 8273db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen @android.view.RemotableViewMethod 8283db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public void setRemoteViewsAdapter(Intent intent) { 8299b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing 8309b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung // service handling the specified intent. 8313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mRemoteViewsAdapter != null) { 8323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Intent.FilterComparison fcNew = new Intent.FilterComparison(intent); 8333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Intent.FilterComparison fcOld = new Intent.FilterComparison( 8343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRemoteViewsAdapter.getRemoteViewsServiceIntent()); 8353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (fcNew.equals(fcOld)) { 8363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return; 8373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8389b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung } 8399b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung 8409b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung // Otherwise, create a new RemoteViewsAdapter for binding 8413db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this); 8423db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 8433db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 8443db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen @Override 8453db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public void setSelection(int position) { 8463db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen setDisplayedChild(position); 8473db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 8483db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 8493db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen @Override 8503db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public View getSelectedView() { 85144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen return getViewAtRelativeIndex(mActiveOffset); 8523db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 8533db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 8543db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 8553db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Called back when the adapter connects to the RemoteViewsService. 8563db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 8573db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public void onRemoteAdapterConnected() { 8583db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen if (mRemoteViewsAdapter != mAdapter) { 8593db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen setAdapter(mRemoteViewsAdapter); 8603db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 8613db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 8623db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen 8633db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen /** 8643db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen * Called back when the adapter disconnects from the RemoteViewsService. 8653db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen */ 8663db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen public void onRemoteAdapterDisconnected() { 8673db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen if (mRemoteViewsAdapter != mAdapter) { 8683db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen mRemoteViewsAdapter = null; 8693db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen setAdapter(mRemoteViewsAdapter); 8703db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 8713db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen } 8723db40678d33c2b5f90c380966d36b3e10ed11f05Adam Cohen} 873