android UI 优化之 AbsListView之深度优化

本文介绍了一种优化Android中ListView和GridView性能的方法,通过禁用fadingedge效果来减少滚动时的延迟,特别是在性能较低的设备上。该方案通过对AbsListView.java进行修改,在滚动时关闭透明渐变边框,待停止滚动后再恢复,从而显著提升了用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


原文:http://blog.sina.com.cn/s/blog_4a6c59d60100pjqc.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值