summaryrefslogtreecommitdiffstats
path: root/core/java/android/widget/AdapterViewAnimator.java
diff options
context:
space:
mode:
authorAdam Cohen <adamcohen@google.com>2010-09-28 14:53:47 -0700
committerAdam Cohen <adamcohen@google.com>2010-10-01 17:18:43 -0700
commit1b065cd1401253f999caa5d0ac12909407cef00e (patch)
tree6d26e9c2477308aa2a08c3f866f4a16d6b000412 /core/java/android/widget/AdapterViewAnimator.java
parentefb8a3d6e058c8bc90bb9da6c58dde453f359851 (diff)
downloadframeworks_base-1b065cd1401253f999caa5d0ac12909407cef00e.zip
frameworks_base-1b065cd1401253f999caa5d0ac12909407cef00e.tar.gz
frameworks_base-1b065cd1401253f999caa5d0ac12909407cef00e.tar.bz2
Added looping parameter to AdapterViewAnimator / StackView
Change-Id: Iad36f7c0e657ffbae6dd17d0c9464ddf12de8b7b
Diffstat (limited to 'core/java/android/widget/AdapterViewAnimator.java')
-rw-r--r--core/java/android/widget/AdapterViewAnimator.java141
1 files changed, 82 insertions, 59 deletions
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index b4efb92..9983d54 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -17,6 +17,7 @@
package android.widget;
import java.util.ArrayList;
+import java.util.HashMap;
import android.animation.ObjectAnimator;
import android.content.Context;
@@ -40,6 +41,7 @@ import android.view.animation.AnimationUtils;
* @attr ref android.R.styleable#AdapterViewAnimator_inAnimation
* @attr ref android.R.styleable#AdapterViewAnimator_outAnimation
* @attr ref android.R.styleable#AdapterViewAnimator_animateFirstView
+ * @attr ref android.R.styleable#AdapterViewAnimator_loopViews
*/
public abstract class AdapterViewAnimator extends AdapterView<Adapter>
implements RemoteViewsAdapter.RemoteAdapterConnectionCallback {
@@ -69,15 +71,14 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
int mNumActiveViews = 1;
/**
- * Array of the children of the {@link AdapterViewAnimator}. This array
- * is accessed in a circular fashion
+ * Map of the children of the {@link AdapterViewAnimator}.
*/
- View[] mActiveViews;
+ private HashMap<Integer, ViewAndIndex> mViewsMap = new HashMap<Integer, ViewAndIndex>();
/**
* List of views pending removal from the {@link AdapterViewAnimator}
*/
- ArrayList<View> mPreviousViews;
+ ArrayList<Integer> mPreviousViews;
/**
* The index, relative to the adapter, of the beginning of the window of views
@@ -124,7 +125,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
* Specifies if the animator should wrap from 0 to the end and vice versa
* or have hard boundaries at the beginning and end
*/
- boolean mShouldLoop = true;
+ boolean mLoopViews = true;
/**
* The width and height of some child, used as a size reference in-case our
@@ -149,22 +150,25 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
- com.android.internal.R.styleable.ViewAnimator);
+ com.android.internal.R.styleable.AdapterViewAnimator);
int resource = a.getResourceId(
- com.android.internal.R.styleable.ViewAnimator_inAnimation, 0);
+ com.android.internal.R.styleable.AdapterViewAnimator_inAnimation, 0);
if (resource > 0) {
setInAnimation(context, resource);
}
- resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_outAnimation, 0);
+ resource = a.getResourceId(com.android.internal.R.styleable.AdapterViewAnimator_outAnimation, 0);
if (resource > 0) {
setOutAnimation(context, resource);
}
boolean flag = a.getBoolean(
- com.android.internal.R.styleable.ViewAnimator_animateFirstView, true);
+ com.android.internal.R.styleable.AdapterViewAnimator_animateFirstView, true);
setAnimateFirstView(flag);
+ mLoopViews = a.getBoolean(
+ com.android.internal.R.styleable.AdapterViewAnimator_loopViews, false);
+
a.recycle();
initViewAnimator();
@@ -175,11 +179,19 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
*/
private void initViewAnimator() {
mMainQueue = new Handler(Looper.myLooper());
- mActiveViews = new View[mNumActiveViews];
- mPreviousViews = new ArrayList<View>();
+ mPreviousViews = new ArrayList<Integer>();
mViewsToBringToFront = new ArrayList<View>();
}
+ private class ViewAndIndex {
+ ViewAndIndex(View v, int i) {
+ view = v;
+ index = i;
+ }
+ View view;
+ int index;
+ }
+
/**
* This method is used by subclasses to configure the animator to display the
* desired number of views, and specify the offset
@@ -193,18 +205,17 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
* @param shouldLoop If the animator is show view 0, and setPrevious() is called, do we
* we loop back to the end, or do we do nothing
*/
- void configureViewAnimator(int numVisibleViews, int activeOffset, boolean shouldLoop) {
+ void configureViewAnimator(int numVisibleViews, int activeOffset) {
if (activeOffset > numVisibleViews - 1) {
// Throw an exception here.
}
mNumActiveViews = numVisibleViews;
mActiveOffset = activeOffset;
- mActiveViews = new View[mNumActiveViews];
mPreviousViews.clear();
+ mViewsMap.clear();
removeAllViewsInLayout();
mCurrentWindowStart = 0;
mCurrentWindowEnd = -1;
- mShouldLoop = shouldLoop;
}
/**
@@ -238,9 +249,9 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
if (mAdapter != null) {
mWhichChild = whichChild;
if (whichChild >= mAdapter.getCount()) {
- mWhichChild = mShouldLoop ? 0 : mAdapter.getCount() - 1;
+ mWhichChild = mLoopViews ? 0 : mAdapter.getCount() - 1;
} else if (whichChild < 0) {
- mWhichChild = mShouldLoop ? mAdapter.getCount() - 1 : 0;
+ mWhichChild = mLoopViews ? mAdapter.getCount() - 1 : 0;
}
boolean hasFocus = getFocusedChild() != null;
@@ -323,9 +334,10 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
* @return View at this index, null if the index is outside the bounds
*/
View getViewAtRelativeIndex(int relativeIndex) {
- if (relativeIndex >= 0 && relativeIndex <= mNumActiveViews - 1) {
- int index = mCurrentWindowStartUnbounded + relativeIndex;
- return mActiveViews[modulo(index, mNumActiveViews)];
+ if (relativeIndex >= 0 && relativeIndex <= mNumActiveViews - 1 && mAdapter != null) {
+ int adapterCount = mAdapter.getCount();
+ int i = modulo(mCurrentWindowStartUnbounded + relativeIndex, adapterCount);
+ return mViewsMap.get(i).view;
}
return null;
}
@@ -346,8 +358,8 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
// get the fresh child from the adapter
View updatedChild = mAdapter.getView(i, null, this);
- if (mActiveViews[index] != null) {
- FrameLayout fl = (FrameLayout) mActiveViews[index];
+ if (mViewsMap.containsKey(index)) {
+ FrameLayout fl = (FrameLayout) mViewsMap.get(index).view;
// flush out the old child
fl.removeAllViewsInLayout();
// add the new child to the frame, if it exists
@@ -373,7 +385,8 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
if (mAdapter == null) return;
for (int i = 0; i < mPreviousViews.size(); i++) {
- View viewToRemove = mPreviousViews.get(i);
+ View viewToRemove = mViewsMap.get(mPreviousViews.get(i)).view;
+ mViewsMap.remove(mPreviousViews.get(i));
viewToRemove.clearAnimation();
if (viewToRemove instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) viewToRemove;
@@ -386,64 +399,74 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
removeViewInLayout(viewToRemove);
}
mPreviousViews.clear();
+ int adapterCount = mAdapter.getCount();
int newWindowStartUnbounded = childIndex - mActiveOffset;
int newWindowEndUnbounded = newWindowStartUnbounded + mNumActiveViews - 1;
int newWindowStart = Math.max(0, newWindowStartUnbounded);
- int newWindowEnd = Math.min(mAdapter.getCount() - 1, newWindowEndUnbounded);
+ int newWindowEnd = Math.min(adapterCount - 1, newWindowEndUnbounded);
- // This section clears out any items that are in our mActiveViews list
+ if (mLoopViews) {
+ newWindowStart = newWindowStartUnbounded;
+ newWindowEnd = newWindowEndUnbounded;
+ }
+ int rangeStart = modulo(newWindowStart, adapterCount);
+ int rangeEnd = modulo(newWindowEnd, adapterCount);
+
+ boolean wrap = false;
+ if (rangeStart > rangeEnd) {
+ wrap = true;
+ }
+
+ // This section clears out any items that are in our active views list
// but are outside the effective bounds of our window (this is becomes an issue
// at the extremities of the list, eg. where newWindowStartUnbounded < 0 or
// newWindowEndUnbounded > mAdapter.getCount() - 1
- for (int i = newWindowStartUnbounded; i < newWindowEndUnbounded; i++) {
- if (i < newWindowStart || i > newWindowEnd) {
- int index = modulo(i, mNumActiveViews);
- if (mActiveViews[index] != null) {
- View previousView = mActiveViews[index];
- mPreviousViews.add(previousView);
- int previousViewRelativeIndex = modulo(index - mCurrentWindowStart,
- mNumActiveViews);
- animateViewForTransition(previousViewRelativeIndex, -1, previousView);
- mActiveViews[index] = null;
- }
+ for (Integer index : mViewsMap.keySet()) {
+ boolean remove = false;
+ if (!wrap && (index < rangeStart || index > rangeEnd)) {
+ remove = true;
+ } else if (wrap && (index > rangeEnd && index < rangeStart)) {
+ remove = true;
+ }
+
+ if (remove) {
+ View previousView = mViewsMap.get(index).view;
+ int oldRelativeIndex = mViewsMap.get(index).index;
+
+ mPreviousViews.add(index);
+ animateViewForTransition(oldRelativeIndex, -1, previousView);
}
}
// If the window has changed
- if (! (newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd)) {
+ if (!(newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd)) {
// Run through the indices in the new range
for (int i = newWindowStart; i <= newWindowEnd; i++) {
- int oldRelativeIndex = i - mCurrentWindowStartUnbounded;
+ int index = modulo(i, adapterCount);
+ int oldRelativeIndex;
+ if (mViewsMap.containsKey(index)) {
+ oldRelativeIndex = mViewsMap.get(index).index;
+ } else {
+ oldRelativeIndex = -1;
+ }
int newRelativeIndex = i - newWindowStartUnbounded;
- int index = modulo(i, mNumActiveViews);
// If this item is in the current window, great, we just need to apply
// the transform for it's new relative position in the window, and animate
// between it's current and new relative positions
- if (i >= mCurrentWindowStart && i <= mCurrentWindowEnd) {
- View view = mActiveViews[index];
+ boolean inOldRange = mViewsMap.containsKey(index) && !mPreviousViews.contains(index);
+
+ if (inOldRange) {
+ View view = mViewsMap.get(index).view;
+ mViewsMap.get(index).index = newRelativeIndex;
applyTransformForChildAtIndex(view, newRelativeIndex);
animateViewForTransition(oldRelativeIndex, newRelativeIndex, view);
- // Otherwise this view is new, so first we have to displace the view that's
- // taking the new view's place within our cache (a circular array)
+ // Otherwise this view is new to the window
} else {
- if (mActiveViews[index] != null) {
- View previousView = mActiveViews[index];
- mPreviousViews.add(previousView);
- int previousViewRelativeIndex = modulo(index - mCurrentWindowStart,
- mNumActiveViews);
- animateViewForTransition(previousViewRelativeIndex, -1, previousView);
-
- if (mCurrentWindowStart > newWindowStart) {
- mViewsToBringToFront.add(previousView);
- }
- }
-
- // We've cleared a spot for the new view. Get it from the adapter, add it
- // and apply any transform / animation
- View newView = mAdapter.getView(i, null, this);
+ // Get the new view from the adapter, add it and apply any transform / animation
+ View newView = mAdapter.getView(modulo(i, adapterCount), null, this);
// We wrap the new view in a FrameLayout so as to respect the contract
// with the adapter, that is, that we don't modify this view directly
@@ -453,12 +476,12 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
if (newView != null) {
fl.addView(newView);
}
- mActiveViews[index] = fl;
+ mViewsMap.put(index, new ViewAndIndex(fl, newRelativeIndex));
addChild(fl);
applyTransformForChildAtIndex(fl, newRelativeIndex);
animateViewForTransition(-1, newRelativeIndex, fl);
}
- mActiveViews[index].bringToFront();
+ mViewsMap.get(index).view.bringToFront();
}
for (int i = 0; i < mViewsToBringToFront.size(); i++) {