您的位置:首页 > 移动开发 > Android开发

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

2014-11-21 11:03 441 查看
http://blog.csdn.net/lovehong0306

前几天项目需要用到左侧拉出抽屉,想到了/article/1363065.html中提到的多方抽屉,拿来试用了下,发现bug还真不少,最不能忍受的是最后那一下“闪烁”,于是乎,改!
下面将修改过程中遇到的问题及其解决方法分享给大家。

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


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

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

[java] view
plaincopy

<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方法在将抽屉快速抽出时基本不能用

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

究其原因,在这里

[java] view
plaincopy

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轴坐标值(不知道的看这里:/article/10794876.html

由此推想而知

1.点击handle(此时content为GONE),这时的getX()得到的是以handle左上角为原点的坐标

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

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

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

修改为如下即可解决:

[java] view
plaincopy

@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)替换成:

[java] view
plaincopy

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方法代码如下:

[java] view
plaincopy

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;

}

}

其中类似

[java] view
plaincopy

if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)

{

mTrackX = -mContentWidth;

}

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

[java] view
plaincopy

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

[java] view
plaincopy

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: