android-misc-widgets多方抽屉bug修复版 解决“闪烁”问题

来源:互联网 发布:格式化硬盘还能恢复数据吗 编辑:程序博客网 时间:2024/05/16 19:53

http://blog.csdn.net/lovehong0306


前几天项目需要用到左侧拉出抽屉,想到了http://blog.csdn.net/hellogv/article/details/6264706中提到的多方抽屉,拿来试用了下,发现bug还真不少,最不能忍受的是最后那一下“闪烁”,于是乎,改!

下面将修改过程中遇到的问题及其解决方法分享给大家。

首先是出现了如图的情况:

当以光的速度点击handle(就是那个带箭头的Button)并拉出到很远很远的地方 就出先上边那个神奇的现象了

寻找原因,发现是这里的问题

if (tmpX != mTrackX || tmpY != mTrackY){mTrackX = tmpX;mTrackY = tmpY;// invalidate(); //放在此导致极快速滑动至touch区域外界面不刷新(mTrackX、mTrackY均为0)}invalidate();

就拿上边那种情况来讲

当瞬间将handle拉至最大位置,即 tmpX=0 的位置,由于mTrackX默认为0,if条件不成立,执行不到invalidate()方法,页面没有刷新

将invalidate()方法移到if'条件语句之外即可解决问题


下一问题:onFling方法在将抽屉快速抽出时基本不能用

抽出来~滑进去~抽出来~滑进去~     (这个抽屉带弹簧的@_@?!)

究其原因,在这里

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {mState = State.FLYING;mVelocity = mOrientation == VERTICAL? velocityY : velocityX;post(startAnimation);return true;}

mVelocity 使用的是onFling方法传进来的参数velocityX,经多次打印log发现velocityX为负数大致看了下源码,这个速度是基于getX()方法算出来的是
大家都知道,getX()方法是获取以widget左上角为坐标原点计算的X轴坐标值(不知道的看这里:http://blog.csdn.net/lovehong0306/article/details/7451507)
由此推想而知
1.点击handle(此时content为GONE),这时的getX()得到的是以handle左上角为原点的坐标

2.快速滑动以发动onFling方法(快到只有两个event事件发生),这时getX()得到的依然是以handle的左上角为原点的坐标,但是由于content已经可见,handle的位置发生了变化,为抽屉完全抽出时的位置,而action_up事件发生时的getX()得到是在handle原点的左边,即为负值,用此时的X坐标值减去之前得到的那个正的坐标值,结果当然是负的了

3.有负的偏移量和时间,计算出来的速度也就是负的了

这就是为什么拉出抽屉后会滑进去的原因了


修改为如下即可解决:

@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY){mState = State.FLYING;float velocityX2, velocityY2;if (lastRawX == -1 && lastRawY == -1)//见onScroll方法{velocityX2 = (curRawX - e1.getRawX())/ (curEventTime - e1.getEventTime()) * 1000; //  px/svelocityY2 = (curRawY - e1.getRawY())/ (curEventTime - e1.getEventTime()) * 1000;}else{velocityX2 = (curRawX - lastRawX)/ (curEventTime - lastEventTime) * 1000;velocityY2 = (curRawY - lastRawY)/ (curEventTime - lastEventTime) * 1000;}mVelocity = mOrientation == VERTICAL ? velocityY2 : velocityX2;if (Math.abs(mVelocity) > 50){if (mVelocity > 0){mAnimatedAcceleration = mMaximumAcceleration;}else{mAnimatedAcceleration = -mMaximumAcceleration;}long now = SystemClock.uptimeMillis();mAnimationLastTime = now;mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;mAnimating = true;mHandler.removeMessages(MSG_ANIMATE);mHandler.removeMessages(MSG_PREPARE_ANIMATE);mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE),mCurrentAnimationTime);return true;}return false;}

代码就不多做解释了,命名还算规范,应该能看懂,最后那几行是为解决“闪烁”问题的

下面就来说下最棘手的问题——“闪烁”


那么为什么会闪烁呢?

经多次尝试,发现是动画与setVisibility(GONG)冲突,当把动画设置为setFillAfter(true)后即可发现,动画结束后设置控件setVisibility(GONG),消失的不仅仅是content,handle也一同消失了。

由此可知handle在动画结束后先消失再出现,于是就出现了闪烁的效果


那么好办,只要把content和handle分别同时设置动画不就行了,content在动画结束后setVisibility(GONG),handle不setVisibility(GONG)。

But,这么尝试了一下发现,虽然“几乎”同时start动画,毕竟还是有时间间隔的,机子性能越差越明显,content和handle分开了!!!

此法行不通,另想他法


源码真是个好东西,看看SlidingDrawer是怎么实现的

原来如此,没用系统动画,利用handler重复改变控件位置

好,就按照这个思路,结合当前代码,改!

(完整代码稍后贴出)

把所有post(startAnimation)替换成:

long now = SystemClock.uptimeMillis();mAnimationLastTime = now;mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;mAnimating = true;mHandler.removeMessages(MSG_ANIMATE);mHandler.removeMessages(MSG_PREPARE_ANIMATE);mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_PREPARE_ANIMATE),mCurrentAnimationTime);

这段代码基本上是从SlidingDrawer源码copy过来的,MSG_PREPARE_ANIMATE是自己加的

起初不明白ANIMATION_FRAME_DURATION的作用,为什么要延迟呢?

后来发现,这个延迟是留给onLayout方法的,如果不加这个延迟,后边用到的方法就可能在onLayout方法之前调用,也就导致了在onLayout方法之前用到了mContentWidth或者mContentHeight,此时的值为0,这也是为什么要另加MSG_PREPARE_ANIMATE


prepareAnimation方法代码如下:

private void prepareAnimation(){switch (mPosition){case LEFT:if (mIsShrinking){mVelocity = -mMaximumMajorVelocity;mAnimatedAcceleration = -mMaximumAcceleration;}else{mVelocity = mMaximumMajorVelocity;mAnimatedAcceleration = mMaximumAcceleration;if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE){mTrackX = -mContentWidth;}}break;case RIGHT:if (mIsShrinking){mVelocity = mMaximumMajorVelocity;mAnimatedAcceleration = mMaximumAcceleration;}else{mVelocity = -mMaximumMajorVelocity;mAnimatedAcceleration = -mMaximumAcceleration;if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE){mTrackX = mContentWidth;}}break;case TOP:if (mIsShrinking){mVelocity = -mMaximumMajorVelocity;mAnimatedAcceleration = -mMaximumAcceleration;}else{mVelocity = mMaximumMajorVelocity;mAnimatedAcceleration = mMaximumAcceleration;if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE){mTrackY = -mContentHeight;}}break;case BOTTOM:if (mIsShrinking){mVelocity = mMaximumMajorVelocity;mAnimatedAcceleration = mMaximumAcceleration;}else{mVelocity = -mMaximumMajorVelocity;mAnimatedAcceleration = -mMaximumAcceleration;if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE){mTrackY = mContentHeight;}}break;}if (mState == State.TRACKING){if (mIsShrinking){if ((mOrientation == VERTICAL && Math.abs(mTrackY) < mContentHeight / 2)|| (mOrientation == HORIZONTAL && Math.abs(mTrackX) < mContentWidth / 2)){mVelocity = -mVelocity;mAnimatedAcceleration = -mAnimatedAcceleration;mIsShrinking = !mIsShrinking;}}else{if ((mOrientation == VERTICAL && Math.abs(mTrackY) > mContentHeight / 2)|| (mOrientation == HORIZONTAL && Math.abs(mTrackX) > mContentWidth / 2)){mVelocity = -mVelocity;mAnimatedAcceleration = -mAnimatedAcceleration;mIsShrinking = !mIsShrinking;}}}if (mState != State.FLYING && mState != State.TRACKING){mState = State.CLICK;}}

其中类似
if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE){mTrackX = -mContentWidth;}

代码是为解决初次使用控件初始化mTrackX,否则此时单击handle会导致控件直接抽出,无动画效果


if (mState == State.TRACKING){if (mIsShrinking){if ((mOrientation == VERTICAL && Math.abs(mTrackY) < mContentHeight / 2)|| (mOrientation == HORIZONTAL && Math.abs(mTrackX) < mContentWidth / 2)){mVelocity = -mVelocity;mAnimatedAcceleration = -mAnimatedAcceleration;mIsShrinking = !mIsShrinking;}}else{if ((mOrientation == VERTICAL && Math.abs(mTrackY) > mContentHeight / 2)|| (mOrientation == HORIZONTAL && Math.abs(mTrackX) > mContentWidth / 2)){mVelocity = -mVelocity;mAnimatedAcceleration = -mAnimatedAcceleration;mIsShrinking = !mIsShrinking;}}}

这段代码是判断抽屉拉出是否过半,也就是决定控件在松开鼠标时是回到关闭状态还是抽出状态

doAnimation()方法比较简单,没啥可讲的


之前的变量 mDuration mLinearFlying mInterpolator 没用到,因为感觉没啥用,目前的控件已能满足大部分人需求,有特殊需求的请自行添加


下面上完整代码,不懂的可以留言问我

初次写博文,不周之处,还请见谅


Panel.java

package org.miscwidgets.widget;import org.miscwidgets.R;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.drawable.Drawable;import android.os.Handler;import android.os.Message;import android.os.SystemClock;import android.util.AttributeSet;import android.util.Log;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.view.ViewParent;import android.view.GestureDetector.OnGestureListener;import android.view.animation.Interpolator;import android.widget.FrameLayout;import android.widget.LinearLayout;/** *  * Fixed by http://blog.csdn.net/lovehong0306/article/details/7451264 *  */public class Panel extends LinearLayout{private static final String TAG = "Panel";private static final float MAXIMUM_MAJOR_VELOCITY = 200.0f;private static final float MAXIMUM_ACCELERATION = 2000.0f;private static final int MSG_ANIMATE = 1000;private static final int MSG_PREPARE_ANIMATE = 2000;private static final int ANIMATION_FRAME_DURATION = 1000 / 60;private final Handler mHandler = new SlidingHandler();private float mAnimatedAcceleration;private long mAnimationLastTime;private long mCurrentAnimationTime;private boolean mAnimating;private final int mMaximumMajorVelocity;private final int mMaximumAcceleration;private float lastRawX, lastRawY, curRawX, curRawY;private float lastEventTime, curEventTime;/** * Callback invoked when the panel is opened/closed. */public static interface OnPanelListener{/** * Invoked when the panel becomes fully closed. */public void onPanelClosed(Panel panel);/** * Invoked when the panel becomes fully opened. */public void onPanelOpened(Panel panel);}private boolean mIsShrinking;private int mPosition;private int mDuration;private boolean mLinearFlying;private int mHandleId;private int mContentId;private View mHandle;private View mContent;private Drawable mOpenedHandle;private Drawable mClosedHandle;private float mTrackX;private float mTrackY;private float mVelocity;private OnPanelListener panelListener;public static final int TOP = 0;public static final int BOTTOM = 1;public static final int LEFT = 2;public static final int RIGHT = 3;private enum State{ABOUT_TO_ANIMATE, ANIMATING, READY, TRACKING, FLYING, CLICK};private State mState;private Interpolator mInterpolator;private GestureDetector mGestureDetector;private int mContentHeight;private int mContentWidth;private int mOrientation;private float mWeight;private PanelOnGestureListener mGestureListener;private boolean mBringToFront;public Panel(Context context, AttributeSet attrs){super(context, attrs);TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Panel);mDuration = a.getInteger(R.styleable.Panel_animationDuration, 750); // duration defaults to 750 msmPosition = a.getInteger(R.styleable.Panel_position, BOTTOM); // position defaults to BOTTOMmLinearFlying = a.getBoolean(R.styleable.Panel_linearFlying, false); // linearFlying defaults to falsemWeight = a.getFraction(R.styleable.Panel_weight, 0, 1, 0.0f); // weight defaults to 0.0if (mWeight < 0 || mWeight > 1){mWeight = 0.0f;Log.w(TAG, a.getPositionDescription()+ ": weight must be > 0 and <= 1");}mOpenedHandle = a.getDrawable(R.styleable.Panel_openedHandle);mClosedHandle = a.getDrawable(R.styleable.Panel_closedHandle);RuntimeException e = null;mHandleId = a.getResourceId(R.styleable.Panel_handle, 0);if (mHandleId == 0){e = new IllegalArgumentException(a.getPositionDescription()+ ": The handle attribute is required and must refer to a valid child.");}mContentId = a.getResourceId(R.styleable.Panel_content, 0);if (mContentId == 0){e = new IllegalArgumentException(a.getPositionDescription()+ ": The content attribute is required and must refer to a valid child.");}a.recycle();final float density = getResources().getDisplayMetrics().density;mMaximumMajorVelocity = (int) (MAXIMUM_MAJOR_VELOCITY * density + 0.5f);mMaximumAcceleration = (int) (MAXIMUM_ACCELERATION * density + 0.5f);if (e != null){throw e;}mOrientation = (mPosition == TOP || mPosition == BOTTOM) ? VERTICAL: HORIZONTAL;setOrientation(mOrientation);mState = State.READY;mGestureListener = new PanelOnGestureListener();mGestureDetector = new GestureDetector(mGestureListener);mGestureDetector.setIsLongpressEnabled(false);// i DON'T really know why i need this...setBaselineAligned(false);}/** * Sets the listener that receives a notification when the panel becomes * open/close. *  * @param onPanelListener *            The listener to be notified when the panel is opened/closed. */public void setOnPanelListener(OnPanelListener onPanelListener){panelListener = onPanelListener;}/** * Gets Panel's mHandle *  * @return Panel's mHandle */public View getHandle(){return mHandle;}/** * Gets Panel's mContent *  * @return Panel's mContent */public View getContent(){return mContent;}/** * Sets the acceleration curve for panel's animation. *  * @param i *            The interpolator which defines the acceleration curve */public void setInterpolator(Interpolator i){mInterpolator = i;}/** * Set the opened state of Panel. *  * @param open *            True if Panel is to be opened, false if Panel is to be closed. * @param animate *            True if use animation, false otherwise. *  * @return True if operation was performed, false otherwise. *  */public boolean setOpen(boolean open, boolean animate){if (mState == State.READY && isOpen() ^ open){mIsShrinking = !open;if (animate){mState = State.ABOUT_TO_ANIMATE;if (!mIsShrinking){// this could make flicker so we test mState in// dispatchDraw()// to see if is equal to ABOUT_TO_ANIMATEmContent.setVisibility(VISIBLE);}long now = SystemClock.uptimeMillis();mAnimationLastTime = now;mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;mAnimating = true;mHandler.removeMessages(MSG_ANIMATE);mHandler.removeMessages(MSG_PREPARE_ANIMATE);mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_PREPARE_ANIMATE),mCurrentAnimationTime);}else{mContent.setVisibility(open ? VISIBLE : GONE);postProcess();}return true;}return false;}/** * Returns the opened status for Panel. *  * @return True if Panel is opened, false otherwise. */public boolean isOpen(){return mContent.getVisibility() == VISIBLE;}@Overrideprotected void onFinishInflate(){super.onFinishInflate();mHandle = findViewById(mHandleId);if (mHandle == null){String name = getResources().getResourceEntryName(mHandleId);throw new RuntimeException("Your Panel must have a child View whose id attribute is 'R.id."+ name + "'");}mHandle.setClickable(true);mHandle.setOnTouchListener(touchListener);// mHandle.setOnClickListener(clickListener);mContent = findViewById(mContentId);if (mContent == null){String name = getResources().getResourceEntryName(mHandleId);throw new RuntimeException("Your Panel must have a child View whose id attribute is 'R.id."+ name + "'");}// reposition childrenremoveView(mHandle);removeView(mContent);if (mPosition == TOP || mPosition == LEFT){addView(mContent);addView(mHandle);}else{addView(mHandle);addView(mContent);}if (mClosedHandle != null){mHandle.setBackgroundDrawable(mClosedHandle);}mContent.setClickable(true);mContent.setVisibility(GONE);if (mWeight > 0){ViewGroup.LayoutParams params = mContent.getLayoutParams();if (mOrientation == VERTICAL){params.height = ViewGroup.LayoutParams.FILL_PARENT;}else{params.width = ViewGroup.LayoutParams.FILL_PARENT;}mContent.setLayoutParams(params);}}@Overrideprotected void onAttachedToWindow(){super.onAttachedToWindow();ViewParent parent = getParent();if (parent != null && parent instanceof FrameLayout){mBringToFront = true;}}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){if (mWeight > 0 && mContent.getVisibility() == VISIBLE){View parent = (View) getParent();if (parent != null){if (mOrientation == VERTICAL){heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (parent.getHeight() * mWeight),MeasureSpec.EXACTLY);}else{widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (parent.getWidth() * mWeight),MeasureSpec.EXACTLY);}}}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b){super.onLayout(changed, l, t, r, b);mContentWidth = mContent.getWidth();mContentHeight = mContent.getHeight();}@Overrideprotected void dispatchDraw(Canvas canvas){// String name = getResources().getResourceEntryName(getId());// Log.d(TAG, name + " ispatchDraw " + mState);// this is why 'mState' was added:// avoid flicker before animation startif (mState == State.ABOUT_TO_ANIMATE && !mIsShrinking){int delta = mOrientation == VERTICAL ? mContentHeight: mContentWidth;if (mPosition == LEFT || mPosition == TOP){delta = -delta;}if (mOrientation == VERTICAL){canvas.translate(0, delta);}else{canvas.translate(delta, 0);}}if (mState == State.TRACKING || mState == State.FLYING|| mState == State.CLICK){canvas.translate(mTrackX, mTrackY);}super.dispatchDraw(canvas);}private float ensureRange(float v, int min, int max){v = Math.max(v, min);v = Math.min(v, max);return v;}OnTouchListener touchListener = new OnTouchListener(){public boolean onTouch(View v, MotionEvent event){if (mAnimating){// we are animatingreturn true;// 动画中不响应onTouch事件}int action = event.getAction();if (action == MotionEvent.ACTION_DOWN){if (mBringToFront){bringToFront();}}if (!mGestureDetector.onTouchEvent(event)){if (action == MotionEvent.ACTION_UP){// tup up after scrollinglong now = SystemClock.uptimeMillis();mAnimationLastTime = now;mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;mAnimating = true;mHandler.removeMessages(MSG_ANIMATE);mHandler.removeMessages(MSG_PREPARE_ANIMATE);mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_PREPARE_ANIMATE),mCurrentAnimationTime);}}return false;}};public boolean initChange(){if (mState != State.READY){// we are animating or just about to animatereturn false;}mState = State.ABOUT_TO_ANIMATE;mIsShrinking = mContent.getVisibility() == VISIBLE;if (!mIsShrinking){// this could make flicker so we test mState in dispatchDraw()// to see if is equal to ABOUT_TO_ANIMATEmContent.setVisibility(VISIBLE);}return true;}private void postProcess(){if (mIsShrinking && mClosedHandle != null){mHandle.setBackgroundDrawable(mClosedHandle);}else if (!mIsShrinking && mOpenedHandle != null){mHandle.setBackgroundDrawable(mOpenedHandle);}// invoke listener if anyif (panelListener != null){if (mIsShrinking){panelListener.onPanelClosed(Panel.this);}else{panelListener.onPanelOpened(Panel.this);}}}class PanelOnGestureListener implements OnGestureListener{float scrollY;float scrollX;@Overridepublic boolean onDown(MotionEvent e){scrollX = scrollY = 0;lastRawX = curRawX = lastRawY = curRawY = -1;lastEventTime = curEventTime = -1;initChange();return true;}@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY){mState = State.FLYING;float velocityX2, velocityY2;if (lastRawX == -1 && lastRawY == -1)//见onScroll方法{velocityX2 = (curRawX - e1.getRawX())/ (curEventTime - e1.getEventTime()) * 1000; //  px/svelocityY2 = (curRawY - e1.getRawY())/ (curEventTime - e1.getEventTime()) * 1000;}else{velocityX2 = (curRawX - lastRawX)/ (curEventTime - lastEventTime) * 1000;velocityY2 = (curRawY - lastRawY)/ (curEventTime - lastEventTime) * 1000;}mVelocity = mOrientation == VERTICAL ? velocityY2 : velocityX2;if (Math.abs(mVelocity) > 50){if (mVelocity > 0){mAnimatedAcceleration = mMaximumAcceleration;}else{mAnimatedAcceleration = -mMaximumAcceleration;}long now = SystemClock.uptimeMillis();mAnimationLastTime = now;mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;mAnimating = true;mHandler.removeMessages(MSG_ANIMATE);mHandler.removeMessages(MSG_PREPARE_ANIMATE);mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE),mCurrentAnimationTime);return true;}return false;}@Overridepublic void onLongPress(MotionEvent e){// not used}@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY){mState = State.TRACKING;float tmpY = 0, tmpX = 0;if (mOrientation == VERTICAL){scrollY -= distanceY;if (mPosition == TOP){tmpY = ensureRange(scrollY, -mContentHeight, 0);}else{tmpY = ensureRange(scrollY, 0, mContentHeight);}}else{scrollX -= distanceX;if (mPosition == LEFT){tmpX = ensureRange(scrollX, -mContentWidth, 0);}else{tmpX = ensureRange(scrollX, 0, mContentWidth);}}if (tmpX != mTrackX || tmpY != mTrackY){mTrackX = tmpX;mTrackY = tmpY;// invalidate(); //放在此导致极快速滑动至touch区域外界面不刷新(mTrackX、mTrackY均为0)}invalidate();lastRawX = curRawX;lastRawY = curRawY;lastEventTime = curEventTime;curRawX = e2.getRawX();curRawY = e2.getRawY();curEventTime = e2.getEventTime();return true;}@Overridepublic void onShowPress(MotionEvent e){// not used}@Overridepublic boolean onSingleTapUp(MotionEvent e){// not usedreturn false;}}private void prepareAnimation(){switch (mPosition){case LEFT:if (mIsShrinking){mVelocity = -mMaximumMajorVelocity;mAnimatedAcceleration = -mMaximumAcceleration;}else{mVelocity = mMaximumMajorVelocity;mAnimatedAcceleration = mMaximumAcceleration;if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE){mTrackX = -mContentWidth;}}break;case RIGHT:if (mIsShrinking){mVelocity = mMaximumMajorVelocity;mAnimatedAcceleration = mMaximumAcceleration;}else{mVelocity = -mMaximumMajorVelocity;mAnimatedAcceleration = -mMaximumAcceleration;if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE){mTrackX = mContentWidth;}}break;case TOP:if (mIsShrinking){mVelocity = -mMaximumMajorVelocity;mAnimatedAcceleration = -mMaximumAcceleration;}else{mVelocity = mMaximumMajorVelocity;mAnimatedAcceleration = mMaximumAcceleration;if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE){mTrackY = -mContentHeight;}}break;case BOTTOM:if (mIsShrinking){mVelocity = mMaximumMajorVelocity;mAnimatedAcceleration = mMaximumAcceleration;}else{mVelocity = -mMaximumMajorVelocity;mAnimatedAcceleration = -mMaximumAcceleration;if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE){mTrackY = mContentHeight;}}break;}if (mState == State.TRACKING){if (mIsShrinking){if ((mOrientation == VERTICAL && Math.abs(mTrackY) < mContentHeight / 2)|| (mOrientation == HORIZONTAL && Math.abs(mTrackX) < mContentWidth / 2)){mVelocity = -mVelocity;mAnimatedAcceleration = -mAnimatedAcceleration;mIsShrinking = !mIsShrinking;}}else{if ((mOrientation == VERTICAL && Math.abs(mTrackY) > mContentHeight / 2)|| (mOrientation == HORIZONTAL && Math.abs(mTrackX) > mContentWidth / 2)){mVelocity = -mVelocity;mAnimatedAcceleration = -mAnimatedAcceleration;mIsShrinking = !mIsShrinking;}}}if (mState != State.FLYING && mState != State.TRACKING){mState = State.CLICK;}}private void doAnimation(){if (mAnimating){long now = SystemClock.uptimeMillis();float t = (now - mAnimationLastTime) / 1000.0f; // ms -> sfinal float v = mVelocity; // px/sfinal float a = mAnimatedAcceleration; // px/s/smVelocity = v + (a * t); // px/smAnimationLastTime = now;switch (mPosition){case LEFT:mTrackX = mTrackX + (v * t) + (0.5f * a * t * t); // pxif (mTrackX > 0){mTrackX = 0;mState = State.READY;mAnimating = false;}else if (mTrackX < -mContentWidth){mTrackX = -mContentWidth;mContent.setVisibility(GONE);mState = State.READY;mAnimating = false;}break;case RIGHT:mTrackX = mTrackX + (v * t) + (0.5f * a * t * t);if (mTrackX < 0){mTrackX = 0;mState = State.READY;mAnimating = false;}else if (mTrackX > mContentWidth){mTrackX = mContentWidth;mContent.setVisibility(GONE);mState = State.READY;mAnimating = false;}break;case TOP:mTrackY = mTrackY + (v * t) + (0.5f * a * t * t);if (mTrackY > 0){mTrackY = 0;mState = State.READY;mAnimating = false;}else if (mTrackY < -mContentHeight){mTrackY = -mContentHeight;mContent.setVisibility(GONE);mState = State.READY;mAnimating = false;}break;case BOTTOM:mTrackY = mTrackY + (v * t) + (0.5f * a * t * t);if (mTrackY < 0){mTrackY = 0;mState = State.READY;mAnimating = false;}else if (mTrackY > mContentHeight){mTrackY = mContentHeight;mContent.setVisibility(GONE);mState = State.READY;mAnimating = false;}break;}invalidate();if (!mAnimating){postProcess();return;}mCurrentAnimationTime += ANIMATION_FRAME_DURATION;mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE),mCurrentAnimationTime);}}private class SlidingHandler extends Handler{public void handleMessage(Message m){switch (m.what){case MSG_ANIMATE:doAnimation();break;case MSG_PREPARE_ANIMATE:prepareAnimation();doAnimation();break;}}}}

http://blog.csdn.net/lovehong0306


工程下载地址:

http://download.csdn.net/detail/lovehong0306/4230052