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,你就可以看到比较明显的效果了!