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.有负的偏移量和时间,计算出来的速度也就是负的了
这就是为什么拉出抽屉后会滑进去的原因了
修改为如下即可解决:
@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是自己加的