首页 > Android > android UI 优化之 AbsListView之深度优化

android UI 优化之 AbsListView之深度优化

2011年7月2日
102 views 评论 发表评论

android 提供的很多List控件如 listview、gridview 默认都会显示一个fadingedge的东西,它在View的top和bottom处各显示一个渐变半透的阴影以达到更好的视觉效果,但是这个带来的副作用就是导致在性能不是那么强劲的机器上,一些listview,gridview的拖动会显得很不流畅,因为我们知道绘制带Alpha的图片是最耗时的。

我们的优化思路就是对这个fadingedge做一些修改,当view处于滚动状态时,通过接口setVerticalFadingEdgeEnabled(false)让其不显示fadingedge,当view处于静止状态时,通过接口setVerticalFadingEdgeEnabled(true)恢复显示fadingedge。以上的listview和gridview等控件都是继承与AbsListView,所以我们直接修改framework中的AbsListView.java文件,就可以达到系统级的改动效果了。

具体修改如下:

  1.  
  2. @Override
  3. public boolean onTouchEvent(MotionEvent ev) {
  4. if (!isEnabled()) {
  5. // A disabled view that is clickable still consumes the touch
  6. // events, it just doesn’t respond to them.
  7. return isClickable() || isLongClickable();
  8. }
  9. if (mFastScroller != null) {
  10. boolean intercepted = mFastScroller.onTouchEvent(ev);
  11. if (intercepted) {
  12. return true;
  13. }
  14. }
  15. final int action = ev.getAction();
  16. View v;
  17. int deltaY;
  18. if (mVelocityTracker == null) {
  19. mVelocityTracker = VelocityTracker.obtain();
  20. }
  21. mVelocityTracker.addMovement(ev);
  22. switch (action & MotionEvent.ACTION_MASK) {
  23. case MotionEvent.ACTION_DOWN: {
  24. setVerticalFadingEdgeEnabled(false);
  25. mActivePointerId = ev.getPointerId(0);
  26. final int x = (int) ev.getX();
  27. final int y = (int) ev.getY();
  28. int motionPosition = pointToPosition(x, y);
  29. if (!mDataChanged) {
  30. if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)
  31. && (getAdapter().isEnabled(motionPosition))) {
  32. // User clicked on an actual view (and was not stopping a fling). It might be a
  33. // click or a scroll. Assume it is a click until proven otherwise
  34. mTouchMode = TOUCH_MODE_DOWN;
  35. // FIXME Debounce
  36. if (mPendingCheckForTap == null) {
  37. mPendingCheckForTap = new CheckForTap();
  38. }
  39. postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
  40. } else {
  41. if (ev.getEdgeFlags() != 0 &amp;&amp; motionPosition < 0) {
  42. // If we couldn’t find a view to click on, but the down event was touching
  43. // the edge, we will bail out and try again. This allows the edge correcting
  44. // code in ViewRoot to try to find a nearby view to select
  45. return false;
  46. }
  47. if (mTouchMode == TOUCH_MODE_FLING) {
  48. // Stopped a fling. It is a scroll.
  49. createScrollingCache();
  50. mTouchMode = TOUCH_MODE_SCROLL;
  51. mMotionCorrection = 0;
  52. motionPosition = findMotionRow(y);
  53. reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
  54. }
  55. }
  56. }
  57. if (motionPosition >= 0) {
  58. // Remember where the motion event started
  59. v = getChildAt(motionPosition – mFirstPosition);
  60. mMotionViewOriginalTop = v.getTop();
  61. }
  62. mMotionX = x;
  63. mMotionY = y;
  64. mMotionPosition = motionPosition;
  65. mLastY = Integer.MIN_VALUE;
  66. break;
  67. }
  68. case MotionEvent.ACTION_MOVE: {
  69. final int pointerIndex = ev.findPointerIndex(mActivePointerId);
  70. final int y = (int) ev.getY(pointerIndex);
  71. deltaY = y – mMotionY;
  72. switch (mTouchMode) {
  73. case TOUCH_MODE_DOWN:
  74. case TOUCH_MODE_TAP:
  75. case TOUCH_MODE_DONE_WAITING:
  76. // Check if we have moved far enough that it looks more like a
  77. // scroll than a tap
  78. startScrollIfNeeded(deltaY);
  79. break;
  80. case TOUCH_MODE_SCROLL:
  81. if (PROFILE_SCROLLING) {
  82. if (!mScrollProfilingStarted) {
  83. Debug.startMethodTracing("AbsListViewScroll");
  84. mScrollProfilingStarted = true;
  85. }
  86. }
  87. if (y != mLastY) {
  88. deltaY -= mMotionCorrection;
  89. int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y – mLastY : deltaY;
  90.  
  91. // No need to do all this work if we’re not going to move anyway
  92. boolean atEdge = false;
  93. if (incrementalDeltaY != 0) {
  94. atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
  95. }
  96. // Check to see if we have bumped into the scroll limit
  97. if (atEdge &amp;&amp; getChildCount() > 0) {
  98. // Treat this like we’re starting a new scroll from the current
  99. // position. This will let the user start scrolling back into
  100. // content immediately rather than needing to scroll back to the
  101. // point where they hit the limit first.
  102. int motionPosition = findMotionRow(y);
  103. if (motionPosition >= 0) {
  104. final View motionView = getChildAt(motionPosition – mFirstPosition);
  105. mMotionViewOriginalTop = motionView.getTop();
  106. }
  107. mMotionY = y;
  108. mMotionPosition = motionPosition;
  109. invalidate();
  110. }
  111. mLastY = y;
  112. }
  113. break;
  114. }
  115. break;
  116. }
  117. case MotionEvent.ACTION_UP: {
  118. switch (mTouchMode) {
  119. case TOUCH_MODE_DOWN:
  120. case TOUCH_MODE_TAP:
  121. case TOUCH_MODE_DONE_WAITING:
  122. setVerticalFadingEdgeEnabled(true);
  123. final int motionPosition = mMotionPosition;
  124. final View child = getChildAt(motionPosition – mFirstPosition);
  125. if (child != null &amp;&amp; !child.hasFocusable()) {
  126. if (mTouchMode != TOUCH_MODE_DOWN) {
  127. child.setPressed(false);
  128. }
  129. if (mPerformClick == null) {
  130. mPerformClick = new PerformClick();
  131. }
  132. final AbsListView.PerformClick performClick = mPerformClick;
  133. performClick.mChild = child;
  134. performClick.mClickMotionPosition = motionPosition;
  135. performClick.rememberWindowAttachCount();
  136. mResurrectToPosition = motionPosition;
  137. if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {
  138. final Handler handler = getHandler();
  139. if (handler != null) {
  140. handler.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?
  141. mPendingCheckForTap : mPendingCheckForLongPress);
  142. }
  143. mLayoutMode = LAYOUT_NORMAL;
  144. if (!mDataChanged &amp;&amp; mAdapter.isEnabled(motionPosition)) {
  145. mTouchMode = TOUCH_MODE_TAP;
  146. setSelectedPositionInt(mMotionPosition);
  147. layoutChildren();
  148. child.setPressed(true);
  149. positionSelector(child);
  150. setPressed(true);
  151. if (mSelector != null) {
  152. Drawable d = mSelector.getCurrent();
  153. if (d != null &amp;&amp; d instanceof TransitionDrawable) {
  154. ((TransitionDrawable) d).resetTransition();
  155. }
  156. }
  157. postDelayed(new Runnable() {
  158. public void run() {
  159. child.setPressed(false);
  160. setPressed(false);
  161. if (!mDataChanged) {
  162. post(performClick);
  163. }
  164. mTouchMode = TOUCH_MODE_REST;
  165. }
  166. }, ViewConfiguration.getPressedStateDuration());
  167. } else {
  168. mTouchMode = TOUCH_MODE_REST;
  169. }
  170. return true;
  171. } else if (!mDataChanged &amp;&amp; mAdapter.isEnabled(motionPosition)) {
  172. post(performClick);
  173. }
  174. }
  175. mTouchMode = TOUCH_MODE_REST;
  176. break;
  177. case TOUCH_MODE_SCROLL:
  178. final int childCount = getChildCount();
  179. if (childCount > 0) {
  180. if (mFirstPosition == 0 &amp;&amp; getChildAt(0).getTop() >= mListPadding.top &amp;&amp;
  181. mFirstPosition + childCount < mItemCount &amp;&amp;
  182. getChildAt(childCount – 1).getBottom() <=
  183. getHeight() – mListPadding.bottom) {
  184. mTouchMode = TOUCH_MODE_REST;
  185. reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
  186. setVerticalFadingEdgeEnabled(true);
  187. } else {
  188. final VelocityTracker velocityTracker = mVelocityTracker;
  189. velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
  190. final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
  191.  
  192. if (Math.abs(initialVelocity) > mMinimumVelocity) {
  193. if (mFlingRunnable == null) {
  194. mFlingRunnable = new FlingRunnable();
  195. }
  196. reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
  197.  
  198. mFlingRunnable.start(-initialVelocity);
  199. } else {
  200. mTouchMode = TOUCH_MODE_REST;
  201. reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
  202. setVerticalFadingEdgeEnabled(true);
  203. }
  204. }
  205. } else {
  206. mTouchMode = TOUCH_MODE_REST;
  207. reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
  208. setVerticalFadingEdgeEnabled(true);
  209. }
  210. break;
  211. }
  212. setPressed(false);
  213. // Need to redraw since we probably aren’t drawing the selector anymore
  214. invalidate();
  215. final Handler handler = getHandler();
  216. if (handler != null) {
  217. handler.removeCallbacks(mPendingCheckForLongPress);
  218. }
  219. if (mVelocityTracker != null) {
  220. mVelocityTracker.recycle();
  221. mVelocityTracker = null;
  222. }
  223.  
  224. mActivePointerId = INVALID_POINTER;
  225. if (PROFILE_SCROLLING) {
  226. if (mScrollProfilingStarted) {
  227. Debug.stopMethodTracing();
  228. mScrollProfilingStarted = false;
  229. }
  230. }
  231. break;
  232. }
  233. case MotionEvent.ACTION_CANCEL: {
  234. mTouchMode = TOUCH_MODE_REST;
  235. setPressed(false);
  236. View motionView = this.getChildAt(mMotionPosition – mFirstPosition);
  237. if (motionView != null) {
  238. motionView.setPressed(false);
  239. }
  240. clearScrollingCache();
  241. final Handler handler = getHandler();
  242. if (handler != null) {
  243. handler.removeCallbacks(mPendingCheckForLongPress);
  244. }
  245. if (mVelocityTracker != null) {
  246. mVelocityTracker.recycle();
  247. mVelocityTracker = null;
  248. }
  249.  
  250. mActivePointerId = INVALID_POINTER;
  251. break;
  252. }
  253.  
  254. case MotionEvent.ACTION_POINTER_UP: {
  255. onSecondaryPointerUp(ev);
  256. final int x = mMotionX;
  257. final int y = mMotionY;
  258. final int motionPosition = pointToPosition(x, y);
  259. if (motionPosition >= 0) {
  260. // Remember where the motion event started
  261. v = getChildAt(motionPosition – mFirstPosition);
  262. mMotionViewOriginalTop = v.getTop();
  263. mMotionPosition = motionPosition;
  264. }
  265. mLastY = y;
  266. break;
  267. }
  268. }
  269. return true;
  270. }
  271. ========================================================================
  272. private class FlingRunnable implements Runnable {
  273.  
  274. private final Scroller mScroller;
  275.  
  276. private int mLastFlingY;
  277. FlingRunnable() {
  278. mScroller = new Scroller(getContext());
  279. }
  280. void start(int initialVelocity) {
  281. int initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0;
  282. mLastFlingY = initialY;
  283. mScroller.fling(0, initialY, 0, initialVelocity,
  284. 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE);
  285. mTouchMode = TOUCH_MODE_FLING;
  286. post(this);
  287. if (PROFILE_FLINGING) {
  288. if (!mFlingProfilingStarted) {
  289. Debug.startMethodTracing("AbsListViewFling");
  290. mFlingProfilingStarted = true;
  291. }
  292. }
  293. }
  294. void startScroll(int distance, int duration) {
  295. int initialY = distance < 0 ? Integer.MAX_VALUE : 0;
  296. mLastFlingY = initialY;
  297. mScroller.startScroll(0, initialY, 0, distance, duration);
  298. mTouchMode = TOUCH_MODE_FLING;
  299. post(this);
  300. }
  301. private void endFling() {
  302. mTouchMode = TOUCH_MODE_REST;
  303. reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
  304. clearScrollingCache();
  305. removeCallbacks(this);
  306. if (mPositionScroller != null) {
  307. removeCallbacks(mPositionScroller);
  308. }
  309. }
  310. public void run() {
  311. switch (mTouchMode) {
  312. default:
  313. return;
  314.  
  315. case TOUCH_MODE_FLING: {
  316. if (mItemCount == 0 || getChildCount() == 0) {
  317. endFling();
  318. return;
  319. }
  320. final Scroller scroller = mScroller;
  321. boolean more = scroller.computeScrollOffset();
  322. final int y = scroller.getCurrY();
  323. // Flip sign to convert finger direction to list items direction
  324. // (e.g. finger moving down means list is moving towards the top)
  325. int delta = mLastFlingY – y;
  326. // Pretend that each frame of a fling scroll is a touch scroll
  327. if (delta > 0) {
  328. // List is moving towards the top. Use first view as mMotionPosition
  329. mMotionPosition = mFirstPosition;
  330. final View firstView = getChildAt(0);
  331. mMotionViewOriginalTop = firstView.getTop();
  332. // Don’t fling more than 1 screen
  333. delta = Math.min(getHeight() – mPaddingBottom – mPaddingTop – 1, delta);
  334. } else {
  335. // List is moving towards the bottom. Use last view as mMotionPosition
  336. int offsetToLast = getChildCount()1;
  337. mMotionPosition = mFirstPosition + offsetToLast;
  338. final View lastView = getChildAt(offsetToLast);
  339. mMotionViewOriginalTop = lastView.getTop();
  340. // Don’t fling more than 1 screen
  341. delta = Math.max(-(getHeight() – mPaddingBottom – mPaddingTop – 1), delta);
  342. }
  343. final boolean atEnd = trackMotionScroll(delta, delta);
  344. if (more &amp;&amp; !atEnd) {
  345. invalidate();
  346. mLastFlingY = y;
  347. post(this);
  348. } else {
  349. endFling();
  350. AbsListView.this.setVerticalFadingEdgeEnabled(true);
  351. if (PROFILE_FLINGING) {
  352. if (mFlingProfilingStarted) {
  353. Debug.stopMethodTracing();
  354. mFlingProfilingStarted = false;
  355. }
  356. }
  357. }
  358. break;
  359. }
  360. }
  361. }
  362. }
  363.  

修改后重新编译,在性能稍差的机器上运行,滚动一下listview或者gridview,你就可以看到比较明显的效果了!

转自:http://blog.sina.com.cn/s/blog_4a6c59d60100pjqc.html

纯净水 Android

  1. 目前还没有任何评论.
  1. 目前还没有任何 trackbacks 和 pingbacks.
  • 粤ICP备09032914号