android UI 优化之 AbsListView之深度优化

JavaGG
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文件,就可以达到系统级的改动效果了。 
 
具体修改如下: 
view source
print ?
001     @Override 
002     public boolean onTouchEvent(MotionEvent ev) { 
003         if (!isEnabled()) { 
004             // A disabled view that is clickable still consumes the touch 
005             // events, it just doesn't respond to them. 
006             return isClickable() || isLongClickable(); 
007         
008         if (mFastScroller != null) { 
009             boolean intercepted = mFastScroller.onTouchEvent(ev); 
010             if (intercepted) { 
011                 return true
012             
013         
014         final int action = ev.getAction(); 
015         View v; 
016         int deltaY; 
017         if (mVelocityTracker == null) { 
018             mVelocityTracker = VelocityTracker.obtain(); 
019         
020         mVelocityTracker.addMovement(ev); 
021         switch (action & MotionEvent.ACTION_MASK) { 
022         case MotionEvent.ACTION_DOWN: { 
023             setVerticalFadingEdgeEnabled(false); 
024             mActivePointerId = ev.getPointerId(0); 
025             final int x = (int) ev.getX(); 
026             final int y = (int) ev.getY(); 
027             int motionPosition = pointToPosition(x, y); 
028             if (!mDataChanged) { 
029                 if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0
030                         && (getAdapter().isEnabled(motionPosition))) { 
031                     // User clicked on an actual view (and was not stopping a fling). It might be a 
032                     // click or a scroll. Assume it is a click until proven otherwise 
033                     mTouchMode = TOUCH_MODE_DOWN; 
034                     // FIXME Debounce 
035                     if (mPendingCheckForTap == null) { 
036                         mPendingCheckForTap = new CheckForTap(); 
037                     
038                     postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); 
039                 } else
040                     if (ev.getEdgeFlags() != 0 && motionPosition < 0) { 
041                         // If we couldn't find a view to click on, but the down event was touching 
042                         // the edge, we will bail out and try again. This allows the edge correcting 
043                         // code in ViewRoot to try to find a nearby view to select 
044                         return false
045                     
046                     if (mTouchMode == TOUCH_MODE_FLING) { 
047                         // Stopped a fling. It is a scroll. 
048                         createScrollingCache(); 
049                         mTouchMode = TOUCH_MODE_SCROLL; 
050                         mMotionCorrection = 0
051                         motionPosition = findMotionRow(y); 
052                         reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); 
053                     
054                 
055             
056             if (motionPosition >= 0) { 
057                 // Remember where the motion event started 
058                 v = getChildAt(motionPosition - mFirstPosition); 
059                 mMotionViewOriginalTop = v.getTop(); 
060             
061             mMotionX = x; 
062             mMotionY = y; 
063             mMotionPosition = motionPosition; 
064             mLastY = Integer.MIN_VALUE; 
065             break
066         
067         case MotionEvent.ACTION_MOVE: { 
068             final int pointerIndex = ev.findPointerIndex(mActivePointerId); 
069             final int y = (int) ev.getY(pointerIndex); 
070             deltaY = y - mMotionY; 
071             switch (mTouchMode) { 
072             case TOUCH_MODE_DOWN: 
073             case TOUCH_MODE_TAP: 
074             case TOUCH_MODE_DONE_WAITING: 
075                 // Check if we have moved far enough that it looks more like a 
076                 // scroll than a tap 
077                 startScrollIfNeeded(deltaY); 
078                 break
079             case TOUCH_MODE_SCROLL: 
080                 if (PROFILE_SCROLLING) { 
081                     if (!mScrollProfilingStarted) { 
082                         Debug.startMethodTracing("AbsListViewScroll"); 
083                         mScrollProfilingStarted = true
084                     
085                 
086                 if (y != mLastY) { 
087                     deltaY -= mMotionCorrection; 
088                     int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY; 
089                        
090                     // No need to do all this work if we're not going to move anyway 
091                     boolean atEdge = false
092                     if (incrementalDeltaY != 0) { 
093                         atEdge = trackMotionScroll(deltaY, incrementalDeltaY); 
094                     
095                     // Check to see if we have bumped into the scroll limit 
096                     if (atEdge && getChildCount() > 0) { 
097                         // Treat this like we're starting a new scroll from the current 
098                         // position. This will let the user start scrolling back into 
099                         // content immediately rather than needing to scroll back to the 
100                         // point where they hit the limit first. 
101                         int motionPosition = findMotionRow(y); 
102                         if (motionPosition >= 0) { 
103                             final View motionView = getChildAt(motionPosition - mFirstPosition); 
104                             mMotionViewOriginalTop = motionView.getTop(); 
105                         
106                         mMotionY = y; 
107                         mMotionPosition = motionPosition; 
108                         invalidate(); 
109                     
110                     mLastY = y; 
111                 
112                 break
113             
114             break
115         
116         case MotionEvent.ACTION_UP: { 
117             switch (mTouchMode) { 
118             case TOUCH_MODE_DOWN: 
119             case TOUCH_MODE_TAP: 
120             case TOUCH_MODE_DONE_WAITING: 
121                 setVerticalFadingEdgeEnabled(true); 
122                 final int motionPosition = mMotionPosition; 
123                 final View child = getChildAt(motionPosition - mFirstPosition); 
124                 if (child != null && !child.hasFocusable()) { 
125                     if (mTouchMode != TOUCH_MODE_DOWN) { 
126                         child.setPressed(false); 
127                     
128                     if (mPerformClick == null) { 
129                         mPerformClick = new PerformClick(); 
130                     
131                     final AbsListView.PerformClick performClick = mPerformClick; 
132                     performClick.mChild = child; 
133                     performClick.mClickMotionPosition = motionPosition; 
134                     performClick.rememberWindowAttachCount(); 
135                     mResurrectToPosition = motionPosition; 
136                     if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) { 
137                         final Handler handler = getHandler(); 
138                         if (handler != null) { 
139                             handler.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ? 
140                                     mPendingCheckForTap : mPendingCheckForLongPress); 
141                         
142                         mLayoutMode = LAYOUT_NORMAL; 
143                         if (!mDataChanged && mAdapter.isEnabled(motionPosition)) { 
144                             mTouchMode = TOUCH_MODE_TAP; 
145                             setSelectedPositionInt(mMotionPosition); 
146                             layoutChildren(); 
147                             child.setPressed(true); 
148                             positionSelector(child); 
149                             setPressed(true); 
150                             if (mSelector != null) { 
151                                 Drawable d = mSelector.getCurrent(); 
152                                 if (d != null && d instanceof TransitionDrawable) { 
153                                     ((TransitionDrawable) d).resetTransition(); 
154                                 
155                             
156                             postDelayed(new Runnable() { 
157                                 public void run() { 
158                                     child.setPressed(false); 
159                                     setPressed(false); 
160                                     if (!mDataChanged) { 
161                                         post(performClick); 
162                                     
163                                     mTouchMode = TOUCH_MODE_REST; 
164                                 
165                             }, ViewConfiguration.getPressedStateDuration()); 
166                         } else
167                             mTouchMode = TOUCH_MODE_REST; 
168                         
169                         return true
170                     } else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) { 
171                         post(performClick); 
172                     
173                 
174                 mTouchMode = TOUCH_MODE_REST; 
175                 break
176             case TOUCH_MODE_SCROLL: 
177                 final int childCount = getChildCount(); 
178                 if (childCount > 0) { 
179                     if (mFirstPosition == 0 && getChildAt(0).getTop() >= mListPadding.top && 
180                             mFirstPosition + childCount < mItemCount && 
181                             getChildAt(childCount - 1).getBottom() <= 
182                                     getHeight() - mListPadding.bottom) { 
183                         mTouchMode = TOUCH_MODE_REST; 
184                         reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 
185                         setVerticalFadingEdgeEnabled(true); 
186                     } else
187                         final VelocityTracker velocityTracker = mVelocityTracker; 
188                         velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 
189                         final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId); 
190        
191                         if (Math.abs(initialVelocity) > mMinimumVelocity) { 
192                             if (mFlingRunnable == null) { 
193                                 mFlingRunnable = new FlingRunnable(); 
194                             
195                             reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); 
196                                
197                             mFlingRunnable.start(-initialVelocity); 
198                         } else
199                             mTouchMode = TOUCH_MODE_REST; 
200                             reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 
201                             setVerticalFadingEdgeEnabled(true); 
202                         
203                     
204                 } else
205                     mTouchMode = TOUCH_MODE_REST; 
206                     reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 
207                     setVerticalFadingEdgeEnabled(true); 
208                 
209                 break
210             
211             setPressed(false); 
212             // Need to redraw since we probably aren't drawing the selector anymore 
213             invalidate(); 
214             final Handler handler = getHandler(); 
215             if (handler != null) { 
216                 handler.removeCallbacks(mPendingCheckForLongPress); 
217             
218             if (mVelocityTracker != null) { 
219                 mVelocityTracker.recycle(); 
220                 mVelocityTracker = null
221             
222                
223             mActivePointerId = INVALID_POINTER; 
224             if (PROFILE_SCROLLING) { 
225                 if (mScrollProfilingStarted) { 
226                     Debug.stopMethodTracing(); 
227                     mScrollProfilingStarted = false
228                 
229             
230             break
231         
232         case MotionEvent.ACTION_CANCEL: { 
233             mTouchMode = TOUCH_MODE_REST; 
234             setPressed(false); 
235             View motionView = this.getChildAt(mMotionPosition - mFirstPosition); 
236             if (motionView != null) { 
237                 motionView.setPressed(false); 
238             
239             clearScrollingCache(); 
240             final Handler handler = getHandler(); 
241             if (handler != null) { 
242                 handler.removeCallbacks(mPendingCheckForLongPress); 
243             
244             if (mVelocityTracker != null) { 
245                 mVelocityTracker.recycle(); 
246                 mVelocityTracker = null
247             
248                
249             mActivePointerId = INVALID_POINTER; 
250             break
251         
252            
253         case MotionEvent.ACTION_POINTER_UP: { 
254             onSecondaryPointerUp(ev); 
255             final int x = mMotionX; 
256             final int y = mMotionY; 
257             final int motionPosition = pointToPosition(x, y); 
258             if (motionPosition >= 0) { 
259                 // Remember where the motion event started 
260                 v = getChildAt(motionPosition - mFirstPosition); 
261                 mMotionViewOriginalTop = v.getTop(); 
262                 mMotionPosition = motionPosition; 
263             
264             mLastY = y; 
265             break
266         
267         
268         return true
269     
270 ======================================================================== 
271     private class FlingRunnable implements Runnable { 
272            
273         private final Scroller mScroller; 
274            
275         private int mLastFlingY; 
276         FlingRunnable() { 
277             mScroller = new Scroller(getContext()); 
278         
279         void start(int initialVelocity) { 
280             int initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0
281             mLastFlingY = initialY; 
282             mScroller.fling(0, initialY, 0, initialVelocity, 
283                     0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE); 
284             mTouchMode = TOUCH_MODE_FLING; 
285             post(this); 
286             if (PROFILE_FLINGING) { 
287                 if (!mFlingProfilingStarted) { 
288                     Debug.startMethodTracing("AbsListViewFling"); 
289                     mFlingProfilingStarted = true
290                 
291             
292         
293         void startScroll(int distance, int duration) { 
294             int initialY = distance < 0 ? Integer.MAX_VALUE : 0
295             mLastFlingY = initialY; 
296             mScroller.startScroll(0, initialY, 0, distance, duration); 
297             mTouchMode = TOUCH_MODE_FLING; 
298             post(this); 
299         
300         private void endFling() { 
301             mTouchMode = TOUCH_MODE_REST; 
302             reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 
303             clearScrollingCache(); 
304             removeCallbacks(this); 
305             if (mPositionScroller != null) { 
306                 removeCallbacks(mPositionScroller); 
307             
308         
309         public void run() { 
310             switch (mTouchMode) { 
311             default
312                 return
313                    
314             case TOUCH_MODE_FLING: { 
315                 if (mItemCount == 0 || getChildCount() == 0) { 
316                     endFling(); 
317                     return
318                 
319                 final Scroller scroller = mScroller; 
320                 boolean more = scroller.computeScrollOffset(); 
321                 final int y = scroller.getCurrY(); 
322                 // Flip sign to convert finger direction to list items direction 
323                 // (e.g. finger moving down means list is moving towards the top) 
324                 int delta = mLastFlingY - y; 
325                 // Pretend that each frame of a fling scroll is a touch scroll 
326                 if (delta > 0) { 
327                     // List is moving towards the top. Use first view as mMotionPosition 
328                     mMotionPosition = mFirstPosition; 
329                     final View firstView = getChildAt(0); 
330                     mMotionViewOriginalTop = firstView.getTop(); 
331                     // Don't fling more than 1 screen 
332                     delta = Math.min(getHeight() - mPaddingBottom - mPaddingTop - 1, delta); 
333                 } else
334                     // List is moving towards the bottom. Use last view as mMotionPosition 
335                     int offsetToLast = getChildCount() - 1
336                     mMotionPosition = mFirstPosition + offsetToLast; 
337                     final View lastView = getChildAt(offsetToLast); 
338                     mMotionViewOriginalTop = lastView.getTop(); 
339                     // Don't fling more than 1 screen 
340                     delta = Math.max(-(getHeight() - mPaddingBottom - mPaddingTop - 1), delta); 
341                 
342                 final boolean atEnd = trackMotionScroll(delta, delta); 
343                 if (more && !atEnd) { 
344                     invalidate(); 
345                     mLastFlingY = y; 
346                     post(this); 
347                 } else
348                     endFling(); 
349                     AbsListView.this.setVerticalFadingEdgeEnabled(true); 
350                     if (PROFILE_FLINGING) { 
351                         if (mFlingProfilingStarted) { 
352                             Debug.stopMethodTracing(); 
353                             mFlingProfilingStarted = false
354                         
355                     
356                 
357                 break
358             
359             
360         
361     

你可能感兴趣的:(android UI 优化之 AbsListView之深度优化)