首页 > 代码库 > android-misc-widgets多方抽屉bug修复版 解决“闪烁”问题
android-misc-widgets多方抽屉bug修复版 解决“闪烁”问题
http://blog.csdn.net/lovehong0306
前几天项目需要用到左侧拉出抽屉,想到了http://blog.csdn.net/hellogv/article/details/6264706中提到的多方抽屉,拿来试用了下,发现bug还真不少,最不能忍受的是最后那一下“闪烁”,于是乎,改!
下面将修改过程中遇到的问题及其解决方法分享给大家。首先是出现了如图的情况:
当以光的速度点击handle(就是那个带箭头的Button)并拉出到很远很远的地方 就出先上边那个神奇的现象了
寻找原因,发现是这里的问题
- <span style="white-space:pre"> </span>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;
- }
大家都知道,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.有负的偏移量和时间,计算出来的速度也就是负的了
这就是为什么拉出抽屉后会滑进去的原因了
修改为如下即可解决:
- @Override
- public 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/s
- velocityY2 = (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 <span style="white-space:pre"> </span>/ 2))
- {
- mVelocity = -mVelocity;
- mAnimatedAcceleration = -mAnimatedAcceleration;
- mIsShrinking = !mIsShrinking;
- }
- }
- else
- {
- if ((mOrientation == VERTICAL && Math.abs(mTrackY) > mContentHeight / 2)
- || (mOrientation == HORIZONTAL && Math.abs(mTrackX) > mContentWidth <span style="white-space:pre"> </span>/ 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 <span style="white-space:pre"> </span>/ 2))
- {
- mVelocity = -mVelocity;
- mAnimatedAcceleration = -mAnimatedAcceleration;
- mIsShrinking = !mIsShrinking;
- }
- }
- else
- {
- if ((mOrientation == VERTICAL && Math.abs(mTrackY) > mContentHeight / 2)
- || (mOrientation == HORIZONTAL && Math.abs(mTrackX) > mContentWidth <span style="white-space:pre"> </span>/ 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 ms
- mPosition = a.getInteger(R.styleable.Panel_position, BOTTOM); // position defaults to BOTTOM
- mLinearFlying = a.getBoolean(R.styleable.Panel_linearFlying, false); // linearFlying defaults to false
- mWeight = a.getFraction(R.styleable.Panel_weight, 0, 1, 0.0f); // weight defaults to 0.0
- if (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_ANIMATE
- mContent.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;
- }
- @Override
- protected 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 children
- removeView(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);
- }
- }
- @Override
- protected void onAttachedToWindow()
- {
- super.onAttachedToWindow();
- ViewParent parent = getParent();
- if (parent != null && parent instanceof FrameLayout)
- {
- mBringToFront = true;
- }
- }
- @Override
- protected 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);
- }
- @Override
- protected 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();
- }
- @Override
- protected 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 start
- if (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 animating
- return 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 scrolling
- 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);
- }
- }
- return false;
- }
- };
- public boolean initChange()
- {
- if (mState != State.READY)
- {
- // we are animating or just about to animate
- return 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_ANIMATE
- mContent.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 any
- if (panelListener != null)
- {
- if (mIsShrinking)
- {
- panelListener.onPanelClosed(Panel.this);
- }
- else
- {
- panelListener.onPanelOpened(Panel.this);
- }
- }
- }
- class PanelOnGestureListener implements OnGestureListener
- {
- float scrollY;
- float scrollX;
- @Override
- public boolean onDown(MotionEvent e)
- {
- scrollX = scrollY = 0;
- lastRawX = curRawX = lastRawY = curRawY = -1;
- lastEventTime = curEventTime = -1;
- initChange();
- return true;
- }
- @Override
- public 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/s
- velocityY2 = (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;
- }
- @Override
- public void onLongPress(MotionEvent e)
- {
- // not used
- }
- @Override
- public 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;
- }
- @Override
- public void onShowPress(MotionEvent e)
- {
- // not used
- }
- @Override
- public boolean onSingleTapUp(MotionEvent e)
- {
- // not used
- return 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 -> s
- final float v = mVelocity; // px/s
- final float a = mAnimatedAcceleration; // px/s/s
- mVelocity = v + (a * t); // px/s
- mAnimationLastTime = now;
- switch (mPosition)
- {
- case LEFT:
- mTrackX = mTrackX + (v * t) + (0.5f * a * t * t); // px
- 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 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
android-misc-widgets多方抽屉bug修复版 解决“闪烁”问题