现在的位置: 首页 > 综合 > 正文

android UI 优化之 AbsListView之深度优化

2013年10月16日 ⁄ 综合 ⁄ 共 9555字 ⁄ 字号 评论关闭

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文件,就可以达到系统级的改动效果了。


具体修改如下:

 

 @Override 
     publicboolean onTouchEvent(MotionEvent ev) { 
         if(!isEnabled()) { 
             //A disabled view that is clickable still consumes thetouch 
             //events, it just doesn't respond to them. 
             returnisClickable() || isLongClickable(); 
         } 
         if(mFastScroller != null) { 
             booleanintercepted =mFastScroller.onTouchEvent(ev); 
             if(intercepted) { 
                 returntrue; 
             } 
         } 
         finalint action = ev.getAction(); 
         Viewv; 
         intdeltaY; 
         if(mVelocityTracker == null) { 
             mVelocityTracker= VelocityTracker.obtain(); 
         } 
         mVelocityTracker.addMovement(ev); 
         switch(action & MotionEvent.ACTION_MASK){ 
         caseMotionEvent.ACTION_DOWN: { 
             setVerticalFadingEdgeEnabled(false); 
             mActivePointerId= ev.getPointerId(0); 
             finalint x = (int) ev.getX(); 
             finalint y = (int) ev.getY(); 
             intmotionPosition = pointToPosition(x, y); 
             if(!mDataChanged) { 
                 if((mTouchMode != TOUCH_MODE_FLING)&& (motionPosition>= 0) 
                         &&(getAdapter().isEnabled(motionPosition))) { 
                     //User clicked on an actual view (and was not stopping a fling). Itmight be a 
                     //click or a scroll. Assume it is a click until provenotherwise 
                     mTouchMode= TOUCH_MODE_DOWN; 
                     //FIXME Debounce 
                     if(mPendingCheckForTap == null) { 
                         mPendingCheckForTap= new CheckForTap(); 
                     } 
                     postDelayed(mPendingCheckForTap,ViewConfiguration.getTapTimeout()); 
                 }else { 
                     if(ev.getEdgeFlags() != 0 &&motionPosition < 0) { 
                         //If we couldn't find a view to click on, but the down event wastouching 
                         //the edge, we will bail out and try again. This allows the edgecorrecting 
                         //code in ViewRoot to try to find a nearby view toselect 
                         returnfalse; 
                     } 
                     if(mTouchMode == TOUCH_MODE_FLING) { 
                         //Stopped a fling. It is a scroll. 
                         createScrollingCache(); 
                         mTouchMode= TOUCH_MODE_SCROLL; 
                         mMotionCorrection= 0; 
                         motionPosition= findMotionRow(y); 
                         reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); 
                     } 
                 } 
             } 
             if(motionPosition >= 0) { 
                 //Remember where the motion event started 
                 v= getChildAt(motionPosition -mFirstPosition); 
                 mMotionViewOriginalTop= v.getTop(); 
             } 
             mMotionX= x; 
             mMotionY= y; 
             mMotionPosition= motionPosition; 
             mLastY= Integer.MIN_VALUE; 
             break; 
         } 
         caseMotionEvent.ACTION_MOVE: { 
             finalint pointerIndex =ev.findPointerIndex(mActivePointerId); 
             finalint y = (int) ev.getY(pointerIndex); 
             deltaY= y - mMotionY; 
             switch(mTouchMode) { 
             caseTOUCH_MODE_DOWN: 
             caseTOUCH_MODE_TAP: 
             caseTOUCH_MODE_DONE_WAITING: 
                 //Check if we have moved far enough that it looks more likea 
                 //scroll than a tap 
                 startScrollIfNeeded(deltaY); 
                 break; 
             caseTOUCH_MODE_SCROLL: 
                 if(PROFILE_SCROLLING) { 
                     if(!mScrollProfilingStarted) { 
                         Debug.startMethodTracing("AbsListViewScroll"); 
                         mScrollProfilingStarted= true; 
                     } 
                 } 
                 if(y != mLastY) { 
                     deltaY-= mMotionCorrection; 
                     intincrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY :deltaY; 
                      
                     //No need to do all this work if we're not going to moveanyway 
                     booleanatEdge = false; 
                     if(incrementalDeltaY != 0) { 
                         atEdge= trackMotionScroll(deltaY,incrementalDeltaY); 
                     } 
                     //Check to see if we have bumped into the scrolllimit 
                     if(atEdge && getChildCount()> 0) { 
                         //Treat this like we're starting a new scroll from thecurrent 
                         //position. This will let the user start scrolling backinto 
                         //content immediately rather than needing to scroll back tothe 
                         //point where they hit the limit first. 
                         intmotionPosition = findMotionRow(y); 
                         if(motionPosition >= 0) { 
                             finalView motionView = getChildAt(motionPosition -mFirstPosition); 
                             mMotionViewOriginalTop= motionView.getTop(); 
                         } 
                         mMotionY= y; 
                         mMotionPosition= motionPosition; 
                         invalidate(); 
                     } 
                     mLastY= y; 
                 } 
                 break; 
             } 
             break; 
         } 
         caseMotionEvent.ACTION_UP: { 
             switch(mTouchMode) { 
             caseTOUCH_MODE_DOWN: 
             caseTOUCH_MODE_TAP: 
             caseTOUCH_MODE_DONE_WAITING: 
                 setVerticalFadingEdgeEnabled(true); 
                 finalint motionPosition = mMotionPosition; 
                 finalView child = getChildAt(motionPosition -mFirstPosition); 
                 if(child != null &&!child.hasFocusable()) { 
                     if(mTouchMode != TOUCH_MODE_DOWN) { 
                         child.setPressed(false); 
                     } 
                     if(mPerformClick == null) { 
                         mPerformClick= new PerformClick(); 
                     } 
                     finalAbsListView.PerformClick performClick =mPerformClick; 
                     performClick.mChild= child; 
                     performClick.mClickMotionPosition= motionPosition; 
                     performClick.rememberWindowAttachCount(); 
                     mResurrectToPosition= motionPosition; 
                     if(mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP){ 
                         finalHandler handler = getHandler(); 
                         if(handler != null) { 
                             handler.removeCallbacks(mTouchMode== TOUCH_MODE_DOWN ? 
                                     mPendingCheckForTap: mPendingCheckForLongPress); 
                         } 
                         mLayoutMode= LAYOUT_NORMAL; 
                         if(!mDataChanged &&mAdapter.isEnabled(motionPosition)) { 
                             mTouchMode= TOUCH_MODE_TAP; 
                             setSelectedPositionInt(mMotionPosition); 
                             layoutChildren(); 
                             child.setPressed(true); 
                             positionSelector(child); 
                             setPressed(true); 
                             if(mSelector != null) { 
                                 Drawabled = mSelector.getCurrent(); 
                                 if(d != null && d instanceofTransitionDrawable) { 
                                     ((TransitionDrawable)d).resetTransition(); 
                                 } 
                             } 
                             postDelayed(newRunnable() { 
                                 publicvoid run() { 
                                     child.setPressed(false); 
                                     setPressed(false); 
                                     if(!mDataChanged) { 
                                         post(performClick); 
                                     } 
                                     mTouchMode= TOUCH_MODE_REST; 
                                 } 
                             },ViewConfiguration.getPressedStateDuration()); 
                         }else { 
                             mTouchMode= TOUCH_MODE_REST; 
                         } 
                         returntrue; 
                     }else if (!mDataChanged &&mAdapter.isEnabled(motionPosition)) { 
                         post(performClick); 
                     } 
                 } 
                 mTouchMode= TOUCH_MODE_REST; 
                 break; 
             caseTOUCH_MODE_SCROLL: 
                 finalint childCount = getChildCount(); 
                 if(childCount > 0) { 
                     if(mFirstPosition == 0 &&getChildAt(0).getTop() >= mListPadding.top&& 
                             mFirstPosition+ childCount < mItemCount&& 
                             getChildAt(childCount- 1).getBottom() <= 
                                     getHeight()- mListPadding.bottom) { 
                         mTouchMode= TOUCH_MODE_REST; 
                         reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 
                         setVerticalFadingEdgeEnabled(true); 
                     }else { 
                         finalVelocityTracker velocityTracker =mVelocityTracker; 
                         velocityTracker.computeCurrentVelocity(1000,mMaximumVelocity); 
                         finalint initialVelocity = (int)velocityTracker.getYVelocity(mActivePointerId); 
      
                         if(Math.abs(initialVelocity) > mMinimumVelocity){ 
                             if(mFlingRunnable == null) { 
                                 mFlingRunnable= new FlingRunnable(); 
                             } 
                             reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); 
                              
                             mFlingRunnable.start(-initialVelocity); 
                         }else { 
                             mTouchMode= TOUCH_MODE_REST; 
                             reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 
                             setVerticalFadingEdgeEnabled(true); 
                         } 
                     } 
                 }else { 
                     mTouchMode= TOUCH_MODE_REST; 
                     reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 
                     setVerticalFadingEdgeEnabled(true); 
                 } 
                 break; 
             } 
             setPressed(false); 
             //Need to redraw since we probably aren't drawing the selectoranymore 
             invalidate(); 
             finalHandler handler = getHandler(); 
             if(handler != null) { 
                 handler.removeCallbacks(mPendingCheckForLongPress); 
             } 
             if(mVelocityTracker != null) { 
                 mVelocityTracker.recycle(); 
                 mVelocityTracker= null; 
             } 
              
             mActivePointerId= INVALID_POINTER; 
             if(PROFILE_SCROLLING) { 
                 if(mScrollProfilingStarted) { 
                     Debug.stopMethodTracing(); 
                     mScrollProfilingStarted= false; 
                 } 
             } 
             break; 
         } 
         caseMotionEvent.ACTION_CANCEL: { 
             mTouchMode= TOUCH_MODE_REST; 
             setPressed(false); 
             ViewmotionView = this.getChildAt(mMotionPosition -mFirstPosition); 
             if(motionView != null) { 
                 motionView.setPressed(false); 
             } 
             clearScrollingCache(); 
             finalHandler handler = getHandler(); 
             if(handler != null) { 
                 handler.removeCallbacks(mPendingCheckForLongPress); 
             } 
             if(mVelocityTracker != null) { 
                 mVelocityTracker.recycle(); 
                 mVelocityTracker= null; 
             } 
              
             mActivePointerId= INVALID_POINTER; 
             break; 
         } 
          
         caseMotionEvent.ACTION_POINTER_UP: { 
             onSecondaryPointerUp(ev); 
             finalint x = mMotionX; 
             finalint y = mMotionY; 
             finalint motionPosition = pointToPosition(x, y); 
             if(motionPosition >= 0) { 
                 //Remember where the motion event started 
                 v= getChildAt(motionPosition -mFirstPosition); 
                 mMotionViewOriginalTop= v.getTop(); 
                 mMotionPosition= motionPosition; 
             } 
             mLastY= y; 
             break; 
         } 
         } 
         returntrue; 
     } 
 ======================================================================== 
     privateclass FlingRunnable implements Runnable { 
          
         privatefinal Scroller mScroller; 
          
         privateint mLastFlingY; 
         FlingRunnable(){ 
             mScroller= new Scroller(getContext()); 
         } 
         voidstart(int initialVelocity) { 
             intinitialY = initialVelocity < 0 ? Integer.MAX_VALUE :0; 
             mLastFlingY= initialY; 
             mScroller.fling(0,initialY, 0, initialVelocity, 
                     0,Integer.MAX_VALUE, 0, Integer.MAX_VALUE); 
             mTouchMode= TOUCH_MODE_FLING; 
             post(this); 
             if(PROFILE_FLINGING) { 
                 if(!mFlingProfilingStarted) { 
                     Debug.startMethodTracing("AbsListViewFling"); 
                     mFlingProfilingStarted= true; 
                 } 
             } 
         } 
         voidstartScroll(int distance, int duration) { 
             intinitialY = distance < 0 ? Integer.MAX_VALUE :0; 
             mLastFlingY= initialY; 
             mScroller.startScroll(0,initialY, 0, distance, duration); 
             mTouchMode= TOUCH_MODE_FLING; 
             post(this); 
         } 
         privatevoid endFling() { 
             mTouchMode= TOUCH_MODE_REST; 
             reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 
             clearScrollingCache(); 
             removeCallbacks(this); 
             if(mPositionScroller != null) { 
                 removeCallbacks(mPositionScroller); 
             } 
         } 
         publicvoid run() { 
             switch(mTouchMode) { 
             default: 
                 return; 
                  
             caseTOUCH_MODE_FLING: { 
                 if(mItemCount == 0 || getChildCount() == 0) { 
                     endFling(); 
                     return; 
                 } 
                 finalScroller scroller = mScroller; 
                 booleanmore = scroller.computeScrollOffset(); 
                 finalint y = scroller.getCurrY(); 
                 //Flip sign to convert finger direction to list itemsdirection 
                 //(e.g. finger moving down means list is moving towards thetop) 
                 intdelta = mLastFlingY - y; 
                 //Pretend that each frame of a fling scroll is a touchscroll 
                 if(delta > 0) { 
                     //List is moving towards the top. Use first view asmMotionPosition 
                     mMotionPosition= mFirstPosition; 
                     finalView firstView = getChildAt(0); 
                     mMotionViewOriginalTop= firstView.getTop(); 
                     //Don't fling more than 1 screen 
                     delta= Math.min(getHeight() - mPaddingBottom - mPaddingTop - 1,delta); 
                 }else { 
                     //List is moving towards the bottom. Use last view asmMotionPosition 
                     intoffsetToLast = getChildCount() - 1; 
                     mMotionPosition= mFirstPosition + offsetToLast; 
                     finalView lastView = getChildAt(offsetToLast); 
                     mMotionViewOriginalTop= lastView.getTop(); 
                     //Don't fling more than 1 screen 
                     delta= Math.max(-(getHeight() - mPaddingBottom - mPaddingTop - 1),delta); 
                 } 
                 finalboolean atEnd = trackMotionScroll(delta,delta); 
                 if(more && !atEnd){ 
                     invalidate(); 
                     mLastFlingY= y; 
                     post(this); 
                 }else { 
                     endFling(); 
                     AbsListView.this.setVerticalFadingEdgeEnabled(true); 
                     if(PROFILE_FLINGING) { 
                         if(mFlingProfilingStarted) { 
                             Debug.stopMethodTracing(); 
                             mFlingProfilingStarted= false; 
                         } 
                     } 
                 } 
                 break; 
             } 
             } 
         } 
     } 
  
 

 

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

 

抱歉!评论已关闭.