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

http://blog.csdn.net/lovehong0306

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

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

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

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

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

[java] view
plain
copy

  1. <span style="white-space:pre">          </span>if (tmpX != mTrackX || tmpY != mTrackY)
  2. {
  3. mTrackX = tmpX;
  4. mTrackY = tmpY;
  5. // invalidate(); //放在此导致极快速滑动至touch区域外界面不刷新(mTrackX、mTrackY均为0)
  6. }
  7. invalidate();

就拿上边那种情况来讲

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

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

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

抽出来~滑进去~抽出来~滑进去~     (这个抽屉带弹簧的@[email protected]?!)

究其原因,在这里

[java] view
plain
copy

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

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.有负的偏移量和时间,计算出来的速度也就是负的了

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

修改为如下即可解决:

[java] view
plain
copy

  1. @Override
  2. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
  3. float velocityY)
  4. {
  5. mState = State.FLYING;
  6. float velocityX2, velocityY2;
  7. if (lastRawX == -1 && lastRawY == -1)   //见onScroll方法
  8. {
  9. velocityX2 = (curRawX - e1.getRawX())
  10. / (curEventTime - e1.getEventTime()) * 1000; //  px/s
  11. velocityY2 = (curRawY - e1.getRawY())
  12. / (curEventTime - e1.getEventTime()) * 1000;
  13. }
  14. else
  15. {
  16. velocityX2 = (curRawX - lastRawX)
  17. / (curEventTime - lastEventTime) * 1000;
  18. velocityY2 = (curRawY - lastRawY)
  19. / (curEventTime - lastEventTime) * 1000;
  20. }
  21. mVelocity = mOrientation == VERTICAL ? velocityY2 : velocityX2;
  22. if (Math.abs(mVelocity) > 50)
  23. {
  24. if (mVelocity > 0)
  25. {
  26. mAnimatedAcceleration = mMaximumAcceleration;
  27. }
  28. else
  29. {
  30. mAnimatedAcceleration = -mMaximumAcceleration;
  31. }
  32. long now = SystemClock.uptimeMillis();
  33. mAnimationLastTime = now;
  34. mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
  35. mAnimating = true;
  36. mHandler.removeMessages(MSG_ANIMATE);
  37. mHandler.removeMessages(MSG_PREPARE_ANIMATE);
  38. mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE),
  39. mCurrentAnimationTime);
  40. return true;
  41. }
  42. return false;
  43. }

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

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

那么为什么会闪烁呢?

经多次尝试,发现是动画与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
plain
copy

  1. long now = SystemClock.uptimeMillis();
  2. mAnimationLastTime = now;
  3. mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
  4. mAnimating = true;
  5. mHandler.removeMessages(MSG_ANIMATE);
  6. mHandler.removeMessages(MSG_PREPARE_ANIMATE);
  7. mHandler.sendMessageAtTime(
  8. mHandler.obtainMessage(MSG_PREPARE_ANIMATE),
  9. mCurrentAnimationTime);

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

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

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

prepareAnimation方法代码如下:

[java] view
plain
copy

  1. private void prepareAnimation()
  2. {
  3. switch (mPosition)
  4. {
  5. case LEFT:
  6. if (mIsShrinking)
  7. {
  8. mVelocity = -mMaximumMajorVelocity;
  9. mAnimatedAcceleration = -mMaximumAcceleration;
  10. }
  11. else
  12. {
  13. mVelocity = mMaximumMajorVelocity;
  14. mAnimatedAcceleration = mMaximumAcceleration;
  15. if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)
  16. {
  17. mTrackX = -mContentWidth;
  18. }
  19. }
  20. break;
  21. case RIGHT:
  22. if (mIsShrinking)
  23. {
  24. mVelocity = mMaximumMajorVelocity;
  25. mAnimatedAcceleration = mMaximumAcceleration;
  26. }
  27. else
  28. {
  29. mVelocity = -mMaximumMajorVelocity;
  30. mAnimatedAcceleration = -mMaximumAcceleration;
  31. if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)
  32. {
  33. mTrackX = mContentWidth;
  34. }
  35. }
  36. break;
  37. case TOP:
  38. if (mIsShrinking)
  39. {
  40. mVelocity = -mMaximumMajorVelocity;
  41. mAnimatedAcceleration = -mMaximumAcceleration;
  42. }
  43. else
  44. {
  45. mVelocity = mMaximumMajorVelocity;
  46. mAnimatedAcceleration = mMaximumAcceleration;
  47. if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)
  48. {
  49. mTrackY = -mContentHeight;
  50. }
  51. }
  52. break;
  53. case BOTTOM:
  54. if (mIsShrinking)
  55. {
  56. mVelocity = mMaximumMajorVelocity;
  57. mAnimatedAcceleration = mMaximumAcceleration;
  58. }
  59. else
  60. {
  61. mVelocity = -mMaximumMajorVelocity;
  62. mAnimatedAcceleration = -mMaximumAcceleration;
  63. if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)
  64. {
  65. mTrackY = mContentHeight;
  66. }
  67. }
  68. break;
  69. }
  70. if (mState == State.TRACKING)
  71. {
  72. if (mIsShrinking)
  73. {
  74. if ((mOrientation == VERTICAL && Math.abs(mTrackY) < mContentHeight / 2)
  75. || (mOrientation == HORIZONTAL && Math.abs(mTrackX) < mContentWidth <span style="white-space:pre">                       </span>/ 2))
  76. {
  77. mVelocity = -mVelocity;
  78. mAnimatedAcceleration = -mAnimatedAcceleration;
  79. mIsShrinking = !mIsShrinking;
  80. }
  81. }
  82. else
  83. {
  84. if ((mOrientation == VERTICAL && Math.abs(mTrackY) > mContentHeight / 2)
  85. || (mOrientation == HORIZONTAL && Math.abs(mTrackX) > mContentWidth <span style="white-space:pre">                       </span>/ 2))
  86. {
  87. mVelocity = -mVelocity;
  88. mAnimatedAcceleration = -mAnimatedAcceleration;
  89. mIsShrinking = !mIsShrinking;
  90. }
  91. }
  92. }
  93. if (mState != State.FLYING && mState != State.TRACKING)
  94. {
  95. mState = State.CLICK;
  96. }
  97. }

其中类似

[java] view
plain
copy

  1. if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)
  2. {
  3. mTrackX = -mContentWidth;
  4. }

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

[java] view
plain
copy

  1. if (mState == State.TRACKING)
  2. {
  3. if (mIsShrinking)
  4. {
  5. if ((mOrientation == VERTICAL && Math.abs(mTrackY) < mContentHeight / 2)
  6. || (mOrientation == HORIZONTAL && Math.abs(mTrackX) < mContentWidth <span style="white-space:pre">                       </span>/ 2))
  7. {
  8. mVelocity = -mVelocity;
  9. mAnimatedAcceleration = -mAnimatedAcceleration;
  10. mIsShrinking = !mIsShrinking;
  11. }
  12. }
  13. else
  14. {
  15. if ((mOrientation == VERTICAL && Math.abs(mTrackY) > mContentHeight / 2)
  16. || (mOrientation == HORIZONTAL && Math.abs(mTrackX) > mContentWidth <span style="white-space:pre">                       </span>/ 2))
  17. {
  18. mVelocity = -mVelocity;
  19. mAnimatedAcceleration = -mAnimatedAcceleration;
  20. mIsShrinking = !mIsShrinking;
  21. }
  22. }
  23. }

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

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

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

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

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

Panel.java

[java] view
plain
copy

  1. package org.miscwidgets.widget;
  2. import org.miscwidgets.R;
  3. import android.content.Context;
  4. import android.content.res.TypedArray;
  5. import android.graphics.Canvas;
  6. import android.graphics.drawable.Drawable;
  7. import android.os.Handler;
  8. import android.os.Message;
  9. import android.os.SystemClock;
  10. import android.util.AttributeSet;
  11. import android.util.Log;
  12. import android.view.GestureDetector;
  13. import android.view.MotionEvent;
  14. import android.view.View;
  15. import android.view.ViewGroup;
  16. import android.view.ViewParent;
  17. import android.view.GestureDetector.OnGestureListener;
  18. import android.view.animation.Interpolator;
  19. import android.widget.FrameLayout;
  20. import android.widget.LinearLayout;
  21. /**
  22. *
  23. * Fixed by http://blog.csdn.net/lovehong0306/article/details/7451264
  24. *
  25. */
  26. public class Panel extends LinearLayout
  27. {
  28. private static final String TAG = "Panel";
  29. private static final float MAXIMUM_MAJOR_VELOCITY = 200.0f;
  30. private static final float MAXIMUM_ACCELERATION = 2000.0f;
  31. private static final int MSG_ANIMATE = 1000;
  32. private static final int MSG_PREPARE_ANIMATE = 2000;
  33. private static final int ANIMATION_FRAME_DURATION = 1000 / 60;
  34. private final Handler mHandler = new SlidingHandler();
  35. private float mAnimatedAcceleration;
  36. private long mAnimationLastTime;
  37. private long mCurrentAnimationTime;
  38. private boolean mAnimating;
  39. private final int mMaximumMajorVelocity;
  40. private final int mMaximumAcceleration;
  41. private float lastRawX, lastRawY, curRawX, curRawY;
  42. private float lastEventTime, curEventTime;
  43. /**
  44. * Callback invoked when the panel is opened/closed.
  45. */
  46. public static interface OnPanelListener
  47. {
  48. /**
  49. * Invoked when the panel becomes fully closed.
  50. */
  51. public void onPanelClosed(Panel panel);
  52. /**
  53. * Invoked when the panel becomes fully opened.
  54. */
  55. public void onPanelOpened(Panel panel);
  56. }
  57. private boolean mIsShrinking;
  58. private int mPosition;
  59. private int mDuration;
  60. private boolean mLinearFlying;
  61. private int mHandleId;
  62. private int mContentId;
  63. private View mHandle;
  64. private View mContent;
  65. private Drawable mOpenedHandle;
  66. private Drawable mClosedHandle;
  67. private float mTrackX;
  68. private float mTrackY;
  69. private float mVelocity;
  70. private OnPanelListener panelListener;
  71. public static final int TOP = 0;
  72. public static final int BOTTOM = 1;
  73. public static final int LEFT = 2;
  74. public static final int RIGHT = 3;
  75. private enum State
  76. {
  77. ABOUT_TO_ANIMATE, ANIMATING, READY, TRACKING, FLYING, CLICK
  78. };
  79. private State mState;
  80. private Interpolator mInterpolator;
  81. private GestureDetector mGestureDetector;
  82. private int mContentHeight;
  83. private int mContentWidth;
  84. private int mOrientation;
  85. private float mWeight;
  86. private PanelOnGestureListener mGestureListener;
  87. private boolean mBringToFront;
  88. public Panel(Context context, AttributeSet attrs)
  89. {
  90. super(context, attrs);
  91. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Panel);
  92. mDuration = a.getInteger(R.styleable.Panel_animationDuration, 750);     // duration defaults to 750 ms
  93. mPosition = a.getInteger(R.styleable.Panel_position, BOTTOM);           // position defaults to BOTTOM
  94. mLinearFlying = a.getBoolean(R.styleable.Panel_linearFlying, false);    // linearFlying defaults to false
  95. mWeight = a.getFraction(R.styleable.Panel_weight, 0, 1, 0.0f);          // weight defaults to 0.0
  96. if (mWeight < 0 || mWeight > 1)
  97. {
  98. mWeight = 0.0f;
  99. Log.w(TAG, a.getPositionDescription()
  100. + ": weight must be > 0 and <= 1");
  101. }
  102. mOpenedHandle = a.getDrawable(R.styleable.Panel_openedHandle);
  103. mClosedHandle = a.getDrawable(R.styleable.Panel_closedHandle);
  104. RuntimeException e = null;
  105. mHandleId = a.getResourceId(R.styleable.Panel_handle, 0);
  106. if (mHandleId == 0)
  107. {
  108. e = new IllegalArgumentException(
  109. a.getPositionDescription()
  110. + ": The handle attribute is required and must refer to a valid child.");
  111. }
  112. mContentId = a.getResourceId(R.styleable.Panel_content, 0);
  113. if (mContentId == 0)
  114. {
  115. e = new IllegalArgumentException(
  116. a.getPositionDescription()
  117. + ": The content attribute is required and must refer to a valid child.");
  118. }
  119. a.recycle();
  120. final float density = getResources().getDisplayMetrics().density;
  121. mMaximumMajorVelocity = (int) (MAXIMUM_MAJOR_VELOCITY * density + 0.5f);
  122. mMaximumAcceleration = (int) (MAXIMUM_ACCELERATION * density + 0.5f);
  123. if (e != null)
  124. {
  125. throw e;
  126. }
  127. mOrientation = (mPosition == TOP || mPosition == BOTTOM) ? VERTICAL
  128. : HORIZONTAL;
  129. setOrientation(mOrientation);
  130. mState = State.READY;
  131. mGestureListener = new PanelOnGestureListener();
  132. mGestureDetector = new GestureDetector(mGestureListener);
  133. mGestureDetector.setIsLongpressEnabled(false);
  134. // i DON‘T really know why i need this...
  135. setBaselineAligned(false);
  136. }
  137. /**
  138. * Sets the listener that receives a notification when the panel becomes
  139. * open/close.
  140. *
  141. * @param onPanelListener
  142. *            The listener to be notified when the panel is opened/closed.
  143. */
  144. public void setOnPanelListener(OnPanelListener onPanelListener)
  145. {
  146. panelListener = onPanelListener;
  147. }
  148. /**
  149. * Gets Panel‘s mHandle
  150. *
  151. * @return Panel‘s mHandle
  152. */
  153. public View getHandle()
  154. {
  155. return mHandle;
  156. }
  157. /**
  158. * Gets Panel‘s mContent
  159. *
  160. * @return Panel‘s mContent
  161. */
  162. public View getContent()
  163. {
  164. return mContent;
  165. }
  166. /**
  167. * Sets the acceleration curve for panel‘s animation.
  168. *
  169. * @param i
  170. *            The interpolator which defines the acceleration curve
  171. */
  172. public void setInterpolator(Interpolator i)
  173. {
  174. mInterpolator = i;
  175. }
  176. /**
  177. * Set the opened state of Panel.
  178. *
  179. * @param open
  180. *            True if Panel is to be opened, false if Panel is to be closed.
  181. * @param animate
  182. *            True if use animation, false otherwise.
  183. *
  184. * @return True if operation was performed, false otherwise.
  185. *
  186. */
  187. public boolean setOpen(boolean open, boolean animate)
  188. {
  189. if (mState == State.READY && isOpen() ^ open)
  190. {
  191. mIsShrinking = !open;
  192. if (animate)
  193. {
  194. mState = State.ABOUT_TO_ANIMATE;
  195. if (!mIsShrinking)
  196. {
  197. // this could make flicker so we test mState in
  198. // dispatchDraw()
  199. // to see if is equal to ABOUT_TO_ANIMATE
  200. mContent.setVisibility(VISIBLE);
  201. }
  202. long now = SystemClock.uptimeMillis();
  203. mAnimationLastTime = now;
  204. mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
  205. mAnimating = true;
  206. mHandler.removeMessages(MSG_ANIMATE);
  207. mHandler.removeMessages(MSG_PREPARE_ANIMATE);
  208. mHandler.sendMessageAtTime(
  209. mHandler.obtainMessage(MSG_PREPARE_ANIMATE),
  210. mCurrentAnimationTime);
  211. }
  212. else
  213. {
  214. mContent.setVisibility(open ? VISIBLE : GONE);
  215. postProcess();
  216. }
  217. return true;
  218. }
  219. return false;
  220. }
  221. /**
  222. * Returns the opened status for Panel.
  223. *
  224. * @return True if Panel is opened, false otherwise.
  225. */
  226. public boolean isOpen()
  227. {
  228. return mContent.getVisibility() == VISIBLE;
  229. }
  230. @Override
  231. protected void onFinishInflate()
  232. {
  233. super.onFinishInflate();
  234. mHandle = findViewById(mHandleId);
  235. if (mHandle == null)
  236. {
  237. String name = getResources().getResourceEntryName(mHandleId);
  238. throw new RuntimeException(
  239. "Your Panel must have a child View whose id attribute is ‘R.id."
  240. + name + "‘");
  241. }
  242. mHandle.setClickable(true);
  243. mHandle.setOnTouchListener(touchListener);
  244. // mHandle.setOnClickListener(clickListener);
  245. mContent = findViewById(mContentId);
  246. if (mContent == null)
  247. {
  248. String name = getResources().getResourceEntryName(mHandleId);
  249. throw new RuntimeException(
  250. "Your Panel must have a child View whose id attribute is ‘R.id."
  251. + name + "‘");
  252. }
  253. // reposition children
  254. removeView(mHandle);
  255. removeView(mContent);
  256. if (mPosition == TOP || mPosition == LEFT)
  257. {
  258. addView(mContent);
  259. addView(mHandle);
  260. }
  261. else
  262. {
  263. addView(mHandle);
  264. addView(mContent);
  265. }
  266. if (mClosedHandle != null)
  267. {
  268. mHandle.setBackgroundDrawable(mClosedHandle);
  269. }
  270. mContent.setClickable(true);
  271. mContent.setVisibility(GONE);
  272. if (mWeight > 0)
  273. {
  274. ViewGroup.LayoutParams params = mContent.getLayoutParams();
  275. if (mOrientation == VERTICAL)
  276. {
  277. params.height = ViewGroup.LayoutParams.FILL_PARENT;
  278. }
  279. else
  280. {
  281. params.width = ViewGroup.LayoutParams.FILL_PARENT;
  282. }
  283. mContent.setLayoutParams(params);
  284. }
  285. }
  286. @Override
  287. protected void onAttachedToWindow()
  288. {
  289. super.onAttachedToWindow();
  290. ViewParent parent = getParent();
  291. if (parent != null && parent instanceof FrameLayout)
  292. {
  293. mBringToFront = true;
  294. }
  295. }
  296. @Override
  297. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
  298. {
  299. if (mWeight > 0 && mContent.getVisibility() == VISIBLE)
  300. {
  301. View parent = (View) getParent();
  302. if (parent != null)
  303. {
  304. if (mOrientation == VERTICAL)
  305. {
  306. heightMeasureSpec = MeasureSpec.makeMeasureSpec(
  307. (int) (parent.getHeight() * mWeight),
  308. MeasureSpec.EXACTLY);
  309. }
  310. else
  311. {
  312. widthMeasureSpec = MeasureSpec.makeMeasureSpec(
  313. (int) (parent.getWidth() * mWeight),
  314. MeasureSpec.EXACTLY);
  315. }
  316. }
  317. }
  318. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  319. }
  320. @Override
  321. protected void onLayout(boolean changed, int l, int t, int r, int b)
  322. {
  323. super.onLayout(changed, l, t, r, b);
  324. mContentWidth = mContent.getWidth();
  325. mContentHeight = mContent.getHeight();
  326. }
  327. @Override
  328. protected void dispatchDraw(Canvas canvas)
  329. {
  330. // String name = getResources().getResourceEntryName(getId());
  331. // Log.d(TAG, name + " ispatchDraw " + mState);
  332. // this is why ‘mState‘ was added:
  333. // avoid flicker before animation start
  334. if (mState == State.ABOUT_TO_ANIMATE && !mIsShrinking)
  335. {
  336. int delta = mOrientation == VERTICAL ? mContentHeight
  337. : mContentWidth;
  338. if (mPosition == LEFT || mPosition == TOP)
  339. {
  340. delta = -delta;
  341. }
  342. if (mOrientation == VERTICAL)
  343. {
  344. canvas.translate(0, delta);
  345. }
  346. else
  347. {
  348. canvas.translate(delta, 0);
  349. }
  350. }
  351. if (mState == State.TRACKING || mState == State.FLYING
  352. || mState == State.CLICK)
  353. {
  354. canvas.translate(mTrackX, mTrackY);
  355. }
  356. super.dispatchDraw(canvas);
  357. }
  358. private float ensureRange(float v, int min, int max)
  359. {
  360. v = Math.max(v, min);
  361. v = Math.min(v, max);
  362. return v;
  363. }
  364. OnTouchListener touchListener = new OnTouchListener()
  365. {
  366. public boolean onTouch(View v, MotionEvent event)
  367. {
  368. if (mAnimating)
  369. {
  370. // we are animating
  371. return true;// 动画中不响应onTouch事件
  372. }
  373. int action = event.getAction();
  374. if (action == MotionEvent.ACTION_DOWN)
  375. {
  376. if (mBringToFront)
  377. {
  378. bringToFront();
  379. }
  380. }
  381. if (!mGestureDetector.onTouchEvent(event))
  382. {
  383. if (action == MotionEvent.ACTION_UP)
  384. {
  385. // tup up after scrolling
  386. long now = SystemClock.uptimeMillis();
  387. mAnimationLastTime = now;
  388. mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
  389. mAnimating = true;
  390. mHandler.removeMessages(MSG_ANIMATE);
  391. mHandler.removeMessages(MSG_PREPARE_ANIMATE);
  392. mHandler.sendMessageAtTime(
  393. mHandler.obtainMessage(MSG_PREPARE_ANIMATE),
  394. mCurrentAnimationTime);
  395. }
  396. }
  397. return false;
  398. }
  399. };
  400. public boolean initChange()
  401. {
  402. if (mState != State.READY)
  403. {
  404. // we are animating or just about to animate
  405. return false;
  406. }
  407. mState = State.ABOUT_TO_ANIMATE;
  408. mIsShrinking = mContent.getVisibility() == VISIBLE;
  409. if (!mIsShrinking)
  410. {
  411. // this could make flicker so we test mState in dispatchDraw()
  412. // to see if is equal to ABOUT_TO_ANIMATE
  413. mContent.setVisibility(VISIBLE);
  414. }
  415. return true;
  416. }
  417. private void postProcess()
  418. {
  419. if (mIsShrinking && mClosedHandle != null)
  420. {
  421. mHandle.setBackgroundDrawable(mClosedHandle);
  422. }
  423. else if (!mIsShrinking && mOpenedHandle != null)
  424. {
  425. mHandle.setBackgroundDrawable(mOpenedHandle);
  426. }
  427. // invoke listener if any
  428. if (panelListener != null)
  429. {
  430. if (mIsShrinking)
  431. {
  432. panelListener.onPanelClosed(Panel.this);
  433. }
  434. else
  435. {
  436. panelListener.onPanelOpened(Panel.this);
  437. }
  438. }
  439. }
  440. class PanelOnGestureListener implements OnGestureListener
  441. {
  442. float scrollY;
  443. float scrollX;
  444. @Override
  445. public boolean onDown(MotionEvent e)
  446. {
  447. scrollX = scrollY = 0;
  448. lastRawX = curRawX = lastRawY = curRawY = -1;
  449. lastEventTime = curEventTime = -1;
  450. initChange();
  451. return true;
  452. }
  453. @Override
  454. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
  455. float velocityY)
  456. {
  457. mState = State.FLYING;
  458. float velocityX2, velocityY2;
  459. if (lastRawX == -1 && lastRawY == -1)   //见onScroll方法
  460. {
  461. velocityX2 = (curRawX - e1.getRawX())
  462. / (curEventTime - e1.getEventTime()) * 1000; //  px/s
  463. velocityY2 = (curRawY - e1.getRawY())
  464. / (curEventTime - e1.getEventTime()) * 1000;
  465. }
  466. else
  467. {
  468. velocityX2 = (curRawX - lastRawX)
  469. / (curEventTime - lastEventTime) * 1000;
  470. velocityY2 = (curRawY - lastRawY)
  471. / (curEventTime - lastEventTime) * 1000;
  472. }
  473. mVelocity = mOrientation == VERTICAL ? velocityY2 : velocityX2;
  474. if (Math.abs(mVelocity) > 50)
  475. {
  476. if (mVelocity > 0)
  477. {
  478. mAnimatedAcceleration = mMaximumAcceleration;
  479. }
  480. else
  481. {
  482. mAnimatedAcceleration = -mMaximumAcceleration;
  483. }
  484. long now = SystemClock.uptimeMillis();
  485. mAnimationLastTime = now;
  486. mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
  487. mAnimating = true;
  488. mHandler.removeMessages(MSG_ANIMATE);
  489. mHandler.removeMessages(MSG_PREPARE_ANIMATE);
  490. mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE),
  491. mCurrentAnimationTime);
  492. return true;
  493. }
  494. return false;
  495. }
  496. @Override
  497. public void onLongPress(MotionEvent e)
  498. {
  499. // not used
  500. }
  501. @Override
  502. public boolean onScroll(MotionEvent e1, MotionEvent e2,
  503. float distanceX, float distanceY)
  504. {
  505. mState = State.TRACKING;
  506. float tmpY = 0, tmpX = 0;
  507. if (mOrientation == VERTICAL)
  508. {
  509. scrollY -= distanceY;
  510. if (mPosition == TOP)
  511. {
  512. tmpY = ensureRange(scrollY, -mContentHeight, 0);
  513. }
  514. else
  515. {
  516. tmpY = ensureRange(scrollY, 0, mContentHeight);
  517. }
  518. }
  519. else
  520. {
  521. scrollX -= distanceX;
  522. if (mPosition == LEFT)
  523. {
  524. tmpX = ensureRange(scrollX, -mContentWidth, 0);
  525. }
  526. else
  527. {
  528. tmpX = ensureRange(scrollX, 0, mContentWidth);
  529. }
  530. }
  531. if (tmpX != mTrackX || tmpY != mTrackY)
  532. {
  533. mTrackX = tmpX;
  534. mTrackY = tmpY;
  535. // invalidate(); //放在此导致极快速滑动至touch区域外界面不刷新(mTrackX、mTrackY均为0)
  536. }
  537. invalidate();
  538. lastRawX = curRawX;
  539. lastRawY = curRawY;
  540. lastEventTime = curEventTime;
  541. curRawX = e2.getRawX();
  542. curRawY = e2.getRawY();
  543. curEventTime = e2.getEventTime();
  544. return true;
  545. }
  546. @Override
  547. public void onShowPress(MotionEvent e)
  548. {
  549. // not used
  550. }
  551. @Override
  552. public boolean onSingleTapUp(MotionEvent e)
  553. {
  554. // not used
  555. return false;
  556. }
  557. }
  558. private void prepareAnimation()
  559. {
  560. switch (mPosition)
  561. {
  562. case LEFT:
  563. if (mIsShrinking)
  564. {
  565. mVelocity = -mMaximumMajorVelocity;
  566. mAnimatedAcceleration = -mMaximumAcceleration;
  567. }
  568. else
  569. {
  570. mVelocity = mMaximumMajorVelocity;
  571. mAnimatedAcceleration = mMaximumAcceleration;
  572. if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)
  573. {
  574. mTrackX = -mContentWidth;
  575. }
  576. }
  577. break;
  578. case RIGHT:
  579. if (mIsShrinking)
  580. {
  581. mVelocity = mMaximumMajorVelocity;
  582. mAnimatedAcceleration = mMaximumAcceleration;
  583. }
  584. else
  585. {
  586. mVelocity = -mMaximumMajorVelocity;
  587. mAnimatedAcceleration = -mMaximumAcceleration;
  588. if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)
  589. {
  590. mTrackX = mContentWidth;
  591. }
  592. }
  593. break;
  594. case TOP:
  595. if (mIsShrinking)
  596. {
  597. mVelocity = -mMaximumMajorVelocity;
  598. mAnimatedAcceleration = -mMaximumAcceleration;
  599. }
  600. else
  601. {
  602. mVelocity = mMaximumMajorVelocity;
  603. mAnimatedAcceleration = mMaximumAcceleration;
  604. if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)
  605. {
  606. mTrackY = -mContentHeight;
  607. }
  608. }
  609. break;
  610. case BOTTOM:
  611. if (mIsShrinking)
  612. {
  613. mVelocity = mMaximumMajorVelocity;
  614. mAnimatedAcceleration = mMaximumAcceleration;
  615. }
  616. else
  617. {
  618. mVelocity = -mMaximumMajorVelocity;
  619. mAnimatedAcceleration = -mMaximumAcceleration;
  620. if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)
  621. {
  622. mTrackY = mContentHeight;
  623. }
  624. }
  625. break;
  626. }
  627. if (mState == State.TRACKING)
  628. {
  629. if (mIsShrinking)
  630. {
  631. if ((mOrientation == VERTICAL && Math.abs(mTrackY) < mContentHeight / 2)
  632. || (mOrientation == HORIZONTAL && Math.abs(mTrackX) < mContentWidth / 2))
  633. {
  634. mVelocity = -mVelocity;
  635. mAnimatedAcceleration = -mAnimatedAcceleration;
  636. mIsShrinking = !mIsShrinking;
  637. }
  638. }
  639. else
  640. {
  641. if ((mOrientation == VERTICAL && Math.abs(mTrackY) > mContentHeight / 2)
  642. || (mOrientation == HORIZONTAL && Math.abs(mTrackX) > mContentWidth / 2))
  643. {
  644. mVelocity = -mVelocity;
  645. mAnimatedAcceleration = -mAnimatedAcceleration;
  646. mIsShrinking = !mIsShrinking;
  647. }
  648. }
  649. }
  650. if (mState != State.FLYING && mState != State.TRACKING)
  651. {
  652. mState = State.CLICK;
  653. }
  654. }
  655. private void doAnimation()
  656. {
  657. if (mAnimating)
  658. {
  659. long now = SystemClock.uptimeMillis();
  660. float t = (now - mAnimationLastTime) / 1000.0f;     // ms -> s
  661. final float v = mVelocity;                          // px/s
  662. final float a = mAnimatedAcceleration;              // px/s/s
  663. mVelocity = v + (a * t);                            // px/s
  664. mAnimationLastTime = now;
  665. switch (mPosition)
  666. {
  667. case LEFT:
  668. mTrackX = mTrackX + (v * t) + (0.5f * a * t * t); // px
  669. if (mTrackX > 0)
  670. {
  671. mTrackX = 0;
  672. mState = State.READY;
  673. mAnimating = false;
  674. }
  675. else if (mTrackX < -mContentWidth)
  676. {
  677. mTrackX = -mContentWidth;
  678. mContent.setVisibility(GONE);
  679. mState = State.READY;
  680. mAnimating = false;
  681. }
  682. break;
  683. case RIGHT:
  684. mTrackX = mTrackX + (v * t) + (0.5f * a * t * t);
  685. if (mTrackX < 0)
  686. {
  687. mTrackX = 0;
  688. mState = State.READY;
  689. mAnimating = false;
  690. }
  691. else if (mTrackX > mContentWidth)
  692. {
  693. mTrackX = mContentWidth;
  694. mContent.setVisibility(GONE);
  695. mState = State.READY;
  696. mAnimating = false;
  697. }
  698. break;
  699. case TOP:
  700. mTrackY = mTrackY + (v * t) + (0.5f * a * t * t);
  701. if (mTrackY > 0)
  702. {
  703. mTrackY = 0;
  704. mState = State.READY;
  705. mAnimating = false;
  706. }
  707. else if (mTrackY < -mContentHeight)
  708. {
  709. mTrackY = -mContentHeight;
  710. mContent.setVisibility(GONE);
  711. mState = State.READY;
  712. mAnimating = false;
  713. }
  714. break;
  715. case BOTTOM:
  716. mTrackY = mTrackY + (v * t) + (0.5f * a * t * t);
  717. if (mTrackY < 0)
  718. {
  719. mTrackY = 0;
  720. mState = State.READY;
  721. mAnimating = false;
  722. }
  723. else if (mTrackY > mContentHeight)
  724. {
  725. mTrackY = mContentHeight;
  726. mContent.setVisibility(GONE);
  727. mState = State.READY;
  728. mAnimating = false;
  729. }
  730. break;
  731. }
  732. invalidate();
  733. if (!mAnimating)
  734. {
  735. postProcess();
  736. return;
  737. }
  738. mCurrentAnimationTime += ANIMATION_FRAME_DURATION;
  739. mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE),
  740. mCurrentAnimationTime);
  741. }
  742. }
  743. private class SlidingHandler extends Handler
  744. {
  745. public void handleMessage(Message m)
  746. {
  747. switch (m.what)
  748. {
  749. case MSG_ANIMATE:
  750. doAnimation();
  751. break;
  752. case MSG_PREPARE_ANIMATE:
  753. prepareAnimation();
  754. doAnimation();
  755. break;
  756. }
  757. }
  758. }
  759. }

http://blog.csdn.net/lovehong0306

工程下载地址:

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

时间: 2025-01-12 09:05:33

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

android-misc-widgets四向(上下左右)抽屉bug修复版--转载

 android-misc-widgets四向(上下左右)抽屉bug修复版 2013-08-04 08:58:13 标签:bug down top panel slidingdrawer 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://mikewang.blog.51cto.com/3826268/1263802 一,与开源项目相比,改进如下: 1,修复了闪屏的问题 二,与开源项目相比,增加的限制: 1,不能设置相应动画的In

Android实际开发中的bug总结与解决方法(一)

                                                                             Android实际开发中的bug总结与解决方法(一) Android开发中有很多bug,我们是完全可以在线下避免的,不要等到线上报的BUG的再去修复.下面是我在实际开发中遇到过的bug和解决方法. BUG 1: java.lang.RuntimeException: Unable to start activity ComponentInfo

[bug修复方案分享]阿拉伯文导致TextView显示顺序左右颠倒

bug现象: 在“?(·ω·) ?”这个表情后通过StringBuilder拼接的文字会显示在表情内部,例如拼接“2015”后TextView控件中显示为“?(·ω·) ?2015” bug原因排查: Step1:怀疑是工程师调用append方法错误,但通过debug调试查看String的char[]值正常 Step2:怀疑是“ ?”这个特殊字符引起的问题,删除后显示正常 Step3:百度搜索“ ?”字符,发现是阿拉伯语文字,结合阿拉伯语从右至左的阅读.书写顺序,确认bug原因 bug修复方法:

懒人听书安卓无限制特别版-发布2.1修复版

件由来:听说现在不流行看书,流行听书了,就去下载了一个使用比较多的软件懒人听书,使用后我喜欢折腾,下载几个东西受到限制.到此你应该想到它的由来了...软件说明:书看多了,眼睛容易累:网逛多了,容易倦.这时候,我们不妨换个方式来阅读,那就是最特殊最新潮的“听书”.如果你真的很懒,或者你是有车一族,而恰巧正堵在了路上,那么懒人听书是你最好不过的选择了,随时下载,随时听书,即可消磨时间,又可增长知识,当然有小朋友的家庭,也是很不错的选择,童话故事很好听.目前提供小说,评书,相声,百家讲坛,少儿读物5大

豆瓣app严重bug修复 给一个普通程序员带来的启发!

整个事情让人觉得不可思议! 之前写过一个帖子<牛逼哄哄的豆瓣公司的程序员!佩服!>地址 http://blog.csdn.net/default7/article/details/38597687,里面提到了豆瓣FM ios版本的一个严重级别的bug,锁屏就无法播放!豆瓣fm 肯定是锁屏可以播放的,但是8月12日更新的app会导致锁屏无法播放. 这个问题一直到8月21日才修复更新!(我相信之间有无数的人在反馈这个bug,看我在CSDN博客上的另外那一篇帖子里面的截图就知道!) 豆瓣FM修复严重

; AutoHotkey全自动安装环境设置和测试JAVA+Eclipas+Android+JRE+JDK+SDK+ADT+Android模拟器+Android Virtual Device Manager+NDK+Studio+Doc+Help+Android Application Project编程调试windows环境[草稿版] DetectHiddenWindows,On SetTitl

; AutoHotkey全自动安装环境设置和测试JAVA+Eclipas+Android+JRE+JDK+SDK+ADT+Android模拟器+Android Virtual Device Manager+NDK+Studio+Doc+Help+Android Application Project编程调试windows环境[草稿版] DetectHiddenWindows,OnSetTitleMatchMode,2 ; 激活窗口并单击按钮IfWinExistActiveControlClick

Android Studio添加包的BUG处理

在开发中,经常遇到Android studio不clean的话就不更新代码. 今天想添加一个包,却怎么也添加不进去,我的版本如下: 添加包怎么也添加不上去 不知道是不是此版本的BUG,我用的是目前最新版的,请知情人士告知. 解决办法:需要去SDK的路径中找到相关的包,然后手动引入,如下图: 找到该包后,按F2,然后复制名称,去Build.gradle中引入,如下: 引入后clean一下,然后就加进去了,删除也是在这里删除. 我认为这个方法虽然可行,但是真心麻烦,感觉应该是目前新版本的BUG,自从

OJ2.0userInfo页面Modify逻辑bug修复,search功能逻辑实现

这周的主要任务:userInfo页面Modify逻辑bug修复,search功能逻辑实现. (一)Modify逻辑bug修复: 这里存在的bug就是在我们不重置密码的时候按照前面的逻辑是不能提交修改,这个逻辑是错误的,应该改为可以不修改密码也能提交,主要是if逻辑判断的修改 先看一下代码: def userInfo(request, user_id): try: user = User.objects.get(userID = request.session['userID']) except:

NHibernate 的 SetResultTransformer 方法在Oracle下的Bug修复

NHibernate 的 SetResultTransformer 方法在Oracle下会出现"Could not find a setter for property"错误,这是Nhibernate在Oracle下使用的一个Bug.针对此Bug我可以自己进行修复. 下载NHibernate源码,将Property下的"ChainedPropertyAccessor.cs"稍作修改就会修复此Bug,代码如下: using System; namespace NHib