-> Added edge behaviour for StackView (what to do at

the beginning and end of the stack)
-> Wrapping children of AdapterViewAnimator in a
   FrameLayout to uphold the Adapter contract (ie.
   don't modify the Adapter's views.)
-> Fixed clipping problem in StackViews with padding

Change-Id: I83b02b5fdfd687838346e0bcb5dc30c033cd0cb8
This commit is contained in:
Adam Cohen
2010-08-18 17:46:23 -07:00
parent 1b196022ec
commit 3d07af0342
2 changed files with 126 additions and 45 deletions

View File

@@ -340,9 +340,14 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
for (int i = 0; i < mPreviousViews.size(); i++) {
View viewToRemove = mPreviousViews.get(i);
viewToRemove.clearAnimation();
if (viewToRemove instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) viewToRemove;
vg.removeAllViewsInLayout();
}
// applyTransformForChildAtIndex here just allows for any cleanup
// associated with this view that may need to be done by a subclass
applyTransformForChildAtIndex(viewToRemove, -1);
removeViewInLayout(viewToRemove);
}
mPreviousViews.clear();
@@ -405,10 +410,14 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
// and apply any transform / animation
View newView = mAdapter.getView(i, null, this);
if (newView != null) {
mActiveViews[index] = newView;
addViewInLayout(newView, -1, createOrReuseLayoutParams(newView));
applyTransformForChildAtIndex(newView, newRelativeIndex);
animateViewForTransition(-1, newRelativeIndex, newView);
// 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
FrameLayout fl = new FrameLayout(mContext);
fl.addView(newView);
mActiveViews[index] = fl;
addViewInLayout(fl, -1, createOrReuseLayoutParams(fl));
applyTransformForChildAtIndex(fl, newRelativeIndex);
animateViewForTransition(-1, newRelativeIndex, fl);
}
}
mActiveViews[index].bringToFront();

View File

@@ -48,7 +48,7 @@ public class StackView extends AdapterViewAnimator {
/**
* Default animation parameters
*/
private final int DEFAULT_ANIMATION_DURATION = 500;
private final int DEFAULT_ANIMATION_DURATION = 400;
private final int MINIMUM_ANIMATION_DURATION = 50;
/**
@@ -141,9 +141,9 @@ public class StackView extends AdapterViewAnimator {
view.setVisibility(VISIBLE);
LayoutParams lp = (LayoutParams) view.getLayoutParams();
int largestDuration =
Math.round(mStackSlider.getDurationForNeutralPosition()*DEFAULT_ANIMATION_DURATION);
int largestDuration = Math.round(
(lp.verticalOffset*1.0f/-mViewHeight)*DEFAULT_ANIMATION_DURATION);
int duration = largestDuration;
if (mYVelocity != 0) {
duration = 1000*(0 - lp.verticalOffset)/Math.abs(mYVelocity);
@@ -165,7 +165,8 @@ public class StackView extends AdapterViewAnimator {
// Slide item out
LayoutParams lp = (LayoutParams) view.getLayoutParams();
int largestDuration = Math.round(mStackSlider.getYProgress()*DEFAULT_ANIMATION_DURATION);
int largestDuration = Math.round(mStackSlider.getDurationForOffscreenPosition()*
DEFAULT_ANIMATION_DURATION);
int duration = largestDuration;
if (mYVelocity != 0) {
duration = 1000*(lp.verticalOffset + mViewHeight)/Math.abs(mYVelocity);
@@ -245,6 +246,7 @@ public class StackView extends AdapterViewAnimator {
// ClipChildren and ClipToPadding. We're probably going to want to reset
// these flags as well.
setClipChildren(false);
setClipToPadding(false);
ViewGroup view = this;
while (view.getParent() != null && view.getParent() instanceof ViewGroup) {
view = (ViewGroup) view.getParent();
@@ -297,22 +299,37 @@ public class StackView extends AdapterViewAnimator {
private void beginGestureIfNeeded(float deltaY) {
if ((int) Math.abs(deltaY) > mTouchSlop && mSwipeGestureType == GESTURE_NONE) {
mSwipeGestureType = deltaY < 0 ? GESTURE_SLIDE_UP : GESTURE_SLIDE_DOWN;
int swipeGestureType = deltaY < 0 ? GESTURE_SLIDE_UP : GESTURE_SLIDE_DOWN;
cancelLongPress();
requestDisallowInterceptTouchEvent(true);
int activeIndex = mSwipeGestureType == GESTURE_SLIDE_DOWN ? mNumActiveViews - 1
int activeIndex = swipeGestureType == GESTURE_SLIDE_DOWN ? mNumActiveViews - 1
: mNumActiveViews - 2;
View v = getViewAtRelativeIndex(activeIndex);
if (v != null) {
mHighlight.setImageBitmap(createOutline(v));
mHighlight.bringToFront();
v.bringToFront();
mStackSlider.setView(v);
if (mSwipeGestureType == GESTURE_SLIDE_DOWN)
v.setVisibility(VISIBLE);
if (mAdapter == null) return;
if (mCurrentWindowStartUnbounded + activeIndex == 0) {
mStackSlider.setMode(StackSlider.BEGINNING_OF_STACK_MODE);
} else if (mCurrentWindowStartUnbounded + activeIndex == mAdapter.getCount()) {
activeIndex--;
mStackSlider.setMode(StackSlider.END_OF_STACK_MODE);
} else {
mStackSlider.setMode(StackSlider.NORMAL_MODE);
}
View v = getViewAtRelativeIndex(activeIndex);
if (v == null) return;
mHighlight.setImageBitmap(createOutline(v));
mHighlight.bringToFront();
v.bringToFront();
mStackSlider.setView(v);
if (swipeGestureType == GESTURE_SLIDE_DOWN)
v.setVisibility(VISIBLE);
// We only register this gesture if we've made it this far without a problem
mSwipeGestureType = swipeGestureType;
}
}
@@ -339,7 +356,7 @@ public class StackView extends AdapterViewAnimator {
case MotionEvent.ACTION_MOVE: {
beginGestureIfNeeded(deltaY);
float rx = 0.3f*deltaX/(mViewHeight*1.0f);
float rx = deltaX/(mViewHeight*1.0f);
if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
float r = (deltaY-mTouchSlop*1.0f)/mViewHeight*1.0f;
mStackSlider.setYProgress(1 - r);
@@ -351,7 +368,6 @@ public class StackView extends AdapterViewAnimator {
mStackSlider.setXProgress(rx);
return true;
}
break;
}
case MotionEvent.ACTION_UP: {
@@ -412,7 +428,7 @@ public class StackView extends AdapterViewAnimator {
}
}
// if we made it this far, it means we didn't find a satisfactory new pointer :(,
// so end the
// so end the gesture
handlePointerUp(ev);
}
}
@@ -422,25 +438,30 @@ public class StackView extends AdapterViewAnimator {
float newY = ev.getY(pointerIndex);
int deltaY = (int) (newY - mInitialY);
mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
mYVelocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
if (mVelocityTracker != null) {
mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
mYVelocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
if (deltaY > mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_DOWN) {
if (deltaY > mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_DOWN
&& mStackSlider.mMode == StackSlider.NORMAL_MODE) {
// Swipe threshold exceeded, swipe down
showNext();
mHighlight.bringToFront();
} else if (deltaY < -mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_UP) {
} else if (deltaY < -mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_UP
&& mStackSlider.mMode == StackSlider.NORMAL_MODE) {
// Swipe threshold exceeded, swipe up
showPrevious();
mHighlight.bringToFront();
} else if (mSwipeGestureType == GESTURE_SLIDE_UP) {
// Didn't swipe up far enough, snap back down
int duration = Math.round(mStackSlider.getYProgress()*DEFAULT_ANIMATION_DURATION);
int duration =
Math.round(mStackSlider.getDurationForNeutralPosition()*DEFAULT_ANIMATION_DURATION);
StackSlider animationSlider = new StackSlider(mStackSlider);
PropertyAnimator snapBackY = new PropertyAnimator(duration, animationSlider,
@@ -453,8 +474,9 @@ public class StackView extends AdapterViewAnimator {
snapBackX.start();
} else if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
// Didn't swipe down far enough, snap back up
int duration = Math.round((1 -
mStackSlider.getYProgress())*DEFAULT_ANIMATION_DURATION);
int duration = Math.round(mStackSlider.getDurationForOffscreenPosition()*
DEFAULT_ANIMATION_DURATION);
StackSlider animationSlider = new StackSlider(mStackSlider);
PropertyAnimator snapBackY = new PropertyAnimator(duration, animationSlider,
"YProgress", mStackSlider.getYProgress(), 1);
@@ -475,6 +497,12 @@ public class StackView extends AdapterViewAnimator {
float mYProgress;
float mXProgress;
static final int NORMAL_MODE = 0;
static final int BEGINNING_OF_STACK_MODE = 1;
static final int END_OF_STACK_MODE = 2;
int mMode = NORMAL_MODE;
public StackSlider() {
}
@@ -482,6 +510,7 @@ public class StackView extends AdapterViewAnimator {
mView = copy.mView;
mYProgress = copy.mYProgress;
mXProgress = copy.mXProgress;
mMode = copy.mMode;
}
private float cubic(float r) {
@@ -525,43 +554,85 @@ public class StackView extends AdapterViewAnimator {
r = Math.max(0, r);
mYProgress = r;
final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();
viewLp.setVerticalOffset(Math.round(-r*mViewHeight));
highlightLp.setVerticalOffset(Math.round(-r*mViewHeight));
mHighlight.setAlpha(highlightAlphaInterpolator(r));
switch (mMode) {
case NORMAL_MODE:
viewLp.setVerticalOffset(Math.round(-r*mViewHeight));
highlightLp.setVerticalOffset(Math.round(-r*mViewHeight));
mHighlight.setAlpha(highlightAlphaInterpolator(r));
float alpha = viewAlphaInterpolator(1-r);
float alpha = viewAlphaInterpolator(1-r);
// We make sure that views which can't be seen (have 0 alpha) are also invisible
// so that they don't interfere with click events.
if (mView.getAlpha() == 0 && alpha != 0 && mView.getVisibility() != VISIBLE) {
mView.setVisibility(VISIBLE);
} else if (alpha == 0 && mView.getAlpha() != 0 && mView.getVisibility() == VISIBLE) {
mView.setVisibility(INVISIBLE);
// We make sure that views which can't be seen (have 0 alpha) are also invisible
// so that they don't interfere with click events.
if (mView.getAlpha() == 0 && alpha != 0 && mView.getVisibility() != VISIBLE) {
mView.setVisibility(VISIBLE);
} else if (alpha == 0 && mView.getAlpha() != 0
&& mView.getVisibility() == VISIBLE) {
mView.setVisibility(INVISIBLE);
}
mView.setAlpha(alpha);
mView.setRotationX(90.0f*rotationInterpolator(r));
mHighlight.setRotationX(90.0f*rotationInterpolator(r));
break;
case BEGINNING_OF_STACK_MODE:
r = r*0.2f;
viewLp.setVerticalOffset(Math.round(-r*mViewHeight));
highlightLp.setVerticalOffset(Math.round(-r*mViewHeight));
mHighlight.setAlpha(highlightAlphaInterpolator(r));
break;
case END_OF_STACK_MODE:
r = (1-r)*0.2f;
viewLp.setVerticalOffset(Math.round(r*mViewHeight));
highlightLp.setVerticalOffset(Math.round(r*mViewHeight));
mHighlight.setAlpha(highlightAlphaInterpolator(r));
break;
}
mView.setAlpha(viewAlphaInterpolator(1-r));
mView.setRotationX(90.0f*rotationInterpolator(r));
mHighlight.setRotationX(90.0f*rotationInterpolator(r));
}
public void setXProgress(float r) {
// enforce r between 0 and 1
r = Math.min(1.0f, r);
r = Math.max(-1.0f, r);
r = Math.min(2.0f, r);
r = Math.max(-2.0f, r);
mXProgress = r;
final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();
r *= 0.2f;
viewLp.setHorizontalOffset(Math.round(r*mViewHeight));
highlightLp.setHorizontalOffset(Math.round(r*mViewHeight));
}
void setMode(int mode) {
mMode = mode;
}
float getDurationForNeutralPosition() {
return getDuration(false);
}
float getDurationForOffscreenPosition() {
return getDuration(mMode == END_OF_STACK_MODE ? false : true);
}
private float getDuration(boolean invert) {
if (mView != null) {
final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
float d = (float) Math.sqrt(Math.pow(viewLp.horizontalOffset,2) +
Math.pow(viewLp.verticalOffset,2));
float maxd = (float) Math.sqrt(Math.pow(mViewHeight, 2) +
Math.pow(0.4f*mViewHeight, 2));
return invert ? (1-d/maxd) : d/maxd;
}
return 0;
}
float getYProgress() {
return mYProgress;
}
@@ -569,6 +640,7 @@ public class StackView extends AdapterViewAnimator {
float getXProgress() {
return mXProgress;
}
}
@Override