android提供的很多List控件如 listview、gridview默认都会显示一个fadingedge的东西,它在View的top和bottom处各显示一个渐变半透的阴影以达到更好的视觉效果,但是这个带来的副作用就是导致在性能不是那么强劲的机器上,一些listview,gridview的拖动会显得很不流畅,因为我们知道绘制带Alpha的图片是最耗时的。
我们的优化思路就是对这个fadingedge做一些修改,当view处于滚动状态时,通过接口setVerticalFadingEdgeEna
具体修改如下:
@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,你就可以看到比较明显的效果了!