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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值