1. 程式人生 > >Android 帶你從原始碼的角度解析Scroller的滾動實現原理

Android 帶你從原始碼的角度解析Scroller的滾動實現原理

今天給大家講解的是Scroller類的滾動實現原理,可能很多朋友不太瞭解該類是用來幹嘛的,但是研究Launcher的朋友應該對他很熟悉,Scroller類是滾動的一個封裝類,可以實現View的平滑滾動效果,什麼是實現View的平滑滾動效果呢,舉個簡單的例子,一個View從在我們指定的時間內從一個位置滾動到另外一個位置,我們利用Scroller類可以實現勻速滾動,可以先加速後減速,可以先減速後加速等等效果,而不是瞬間的移動的效果,所以Scroller可以幫我們實現很多滑動的效果。

在介紹Scroller類之前,我們先去了解View的scrollBy() 和scrollTo()方法的區別,在區分這兩個方法的之前,我們要先理解View 裡面的兩個成員變數mScrollX, mScrollY,X軸方向的偏移量和Y軸方向的偏移量,這個是一個相對距離,相對的不是螢幕的原點,而是View的左邊緣,舉個通俗易懂的例子,一列火車從吉安到深圳,途中經過贛州,那麼原點就是贛州,偏移量就是 負的吉安到贛州的距離,大家從getScrollX()方法中的註釋中就能看出答案來

  1. /** 
  2.     * Return the scrolled left position of this view. This is the left edge of 
  3.     * the displayed part of your view. You do not need to draw any pixels 
  4.     * farther left, since those are outside of the frame of your view on 
  5.     * screen. 
  6.     * 
  7.     * @return The left edge of the displayed part of your view, in pixels.
     
  8.     */
  9.    publicfinalint getScrollX() {  
  10.        return mScrollX;  
  11.    }  
現在我們知道了向右滑動 mScrollX就為負數,向左滑動mScrollX為正數,接下來我們先來看看 scrollTo()方法的原始碼
  1. /** 
  2.    * Set the scrolled position of your view. This will cause a call to 
  3.    * {@link #onScrollChanged(int, int, int, int)} and the view will be 
  4.    * invalidated.
     
  5.    * @param x the x position to scroll to 
  6.    * @param y the y position to scroll to 
  7.    */
  8.   publicvoid scrollTo(int x, int y) {  
  9.       if (mScrollX != x || mScrollY != y) {  
  10.           int oldX = mScrollX;  
  11.           int oldY = mScrollY;  
  12.           mScrollX = x;  
  13.           mScrollY = y;  
  14.           onScrollChanged(mScrollX, mScrollY, oldX, oldY);  
  15.           if (!awakenScrollBars()) {  
  16.               invalidate();  
  17.           }  
  18.       }  
  19.   }  
從該方法中我們可以看出,先判斷傳進來的(x, y)值是否和View的X, Y偏移量相等,如果不相等,就呼叫onScrollChanged()方法來通知介面發生改變,然後重繪介面,所以這樣子就實現了移動效果啦, 現在我們知道了scrollTo()方法是滾動到(x, y)這個偏移量的點,他是相對於View的開始位置來滾動的。在看看scrollBy()這個方法的程式碼
  1. /** 
  2.     * Move the scrolled position of your view. This will cause a call to 
  3.     * {@link #onScrollChanged(int, int, int, int)} and the view will be 
  4.     * invalidated. 
  5.     * @param x the amount of pixels to scroll by horizontally 
  6.     * @param y the amount of pixels to scroll by vertically 
  7.     */
  8.    publicvoid scrollBy(int x, int y) {  
  9.        scrollTo(mScrollX + x, mScrollY + y);  
  10.    }  

原來他裡面呼叫了scrollTo()方法,那就好辦了,他就是相對於View上一個位置根據(x, y)來進行滾動,可能大家腦海中對這兩個方法還有點模糊,沒關係,還是舉個通俗的例子幫大家理解下,假如一個View,呼叫兩次scrollTo(-10, 0),第一次向右滾動10,第二次就不滾動了,因為mScrollX和x相等了,當我們呼叫兩次scrollBy(-10, 0),第一次向右滾動10,第二次再向右滾動10,他是相對View的上一個位置來滾動的。

對於scrollTo()和scrollBy()方法還有一點需要注意,這點也很重要,假如你給一個LinearLayout呼叫scrollTo()方法,並不是LinearLayout滾動,而是LinearLayout裡面的內容進行滾動,比如你想對一個按鈕進行滾動,直接用Button呼叫scrollTo()一定達不到你的需求,大家可以試一試,如果真要對某個按鈕進行scrollTo()滾動的話,我們可以在Button外面包裹一層Layout,然後對Layout呼叫scrollTo()方法。

瞭解了scrollTo()和scrollBy()方法之後我們就瞭解下Scroller類了,先看其構造方法

  1. /** 
  2.  * Create a Scroller with the default duration and interpolator. 
  3.  */
  4. public Scroller(Context context) {  
  5.     this(context, null);  
  6. }  
  7. /** 
  8.  * Create a Scroller with the specified interpolator. If the interpolator is 
  9.  * null, the default (viscous) interpolator will be used. 
  10.  */
  11. public Scroller(Context context, Interpolator interpolator) {  
  12.     mFinished = true;  
  13.     mInterpolator = interpolator;  
  14.     float ppi = context.getResources().getDisplayMetrics().density * 160.0f;  
  15.     mDeceleration = SensorManager.GRAVITY_EARTH   // g (m/s^2)
  16.                   * 39.37f                        // inch/meter
  17.                   * ppi                           // pixels per inch
  18.                   * ViewConfiguration.getScrollFriction();  
  19. }  
只有兩個構造方法,第一個只有一個Context引數,第二個構造方法中指定了Interpolator,什麼Interpolator呢?中文意思插補器,瞭解Android動畫的朋友都應該熟悉
Interpolator,他指定了動畫的變化率,比如說勻速變化,先加速後減速,正弦變化等等,不同的Interpolator可以做出不同的效果出來,第一個使用預設的Interpolator(viscous) 

接下來我們就要在Scroller類裡面找滾動的方法,我們從名字上面可以看出startScroll()應該是個滾動的方法,我們來看看其原始碼吧

  1. publicvoid startScroll(int startX, int startY, int dx, int dy, int duration) {  
  2.     mMode = SCROLL_MODE;  
  3.     mFinished = false;  
  4.     mDuration = duration;  
  5.     mStartTime = AnimationUtils.currentAnimationTimeMillis();  
  6.     mStartX = startX;  
  7.     mStartY = startY;  
  8.     mFinalX = startX + dx;  
  9.     mFinalY = startY + dy;  
  10.     mDeltaX = dx;  
  11.     mDeltaY = dy;  
  12.     mDurationReciprocal = 1.0f / (float) mDuration;  
  13.     // This controls the viscous fluid effect (how much of it)
  14.     mViscousFluidScale = 8.0f;  
  15.     // must be set to 1.0 (used in viscousFluid())
  16.     mViscousFluidNormalize = 1.0f;  
  17.     mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);  
  18. }  
在這個方法中我們只看到了對一些滾動的基本設定動作,比如設定滾動模式,開始時間,持續時間等等,並沒有任何對View的滾動操作,也許你正納悶,不是滾動的方法幹嘛還叫做startScroll(),稍安勿躁,既然叫開始滾動,那就是對滾動的滾動之前的基本設定咯。
  1. /** 
  2.  * Call this when you want to know the new location.  If it returns true, 
  3.  * the animation is not yet finished.  loc will be altered to provide the 
  4.  * new location. 
  5.  */
  6. publicboolean computeScrollOffset() {  
  7.     if (mFinished) {  
  8.         returnfalse;  
  9.     }  
  10.     int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);  
  11.     if (timePassed < mDuration) {  
  12.         switch (mMode) {  
  13.         case SCROLL_MODE:  
  14.             float x = (float)timePassed * mDurationReciprocal;  
  15.             if (mInterpolator == null)  
  16.                 x = viscousFluid(x);   
  17.             else
  18.                 x = mInterpolator.getInterpolation(x);  
  19.             mCurrX = mStartX + Math.round(x * mDeltaX);  
  20.             mCurrY = mStartY + Math.round(x * mDeltaY);  
  21.             break;  
  22.         case FLING_MODE:  
  23.             float timePassedSeconds = timePassed / 1000.0f;  
  24.             float distance = (mVelocity * timePassedSeconds)  
  25.                     - (mDeceleration * timePassedSeconds * timePassedSeconds / 2.0f);  
  26.             mCurrX = mStartX + Math.round(distance * mCoeffX);  
  27.             // Pin to mMinX <= mCurrX <= mMaxX
  28.             mCurrX = Math.min(mCurrX, mMaxX);  
  29.             mCurrX = Math.max(mCurrX, mMinX);  
  30.             mCurrY = mStartY + Math.round(distance * mCoeffY);  
  31.             // Pin to mMinY <= mCurrY <= mMaxY
  32.             mCurrY = Math.min(mCurrY, mMaxY);  
  33.             mCurrY = Math.max(mCurrY, mMinY);  
  34.             break;  
  35.         }  
  36.     }  
  37.     else {  
  38.         mCurrX = mFinalX;  
  39.         mCurrY = mFinalY;  
  40.         mFinished = true;  
  41.     }  
  42.     returntrue;  
  43. }  
我們在startScroll()方法的時候獲取了當前的動畫毫秒賦值給了mStartTime,在computeScrollOffset()中再一次呼叫AnimationUtils.currentAnimationTimeMillis()來獲取動畫
毫秒減去mStartTime就是持續時間了,然後進去if判斷,如果動畫持續時間小於我們設定的滾動持續時間mDuration,進去switch的SCROLL_MODE,然後根據Interpolator來計算出在該時間段裡面移動的距離,賦值給mCurrX, mCurrY, 所以該方法的作用是,計算在0到mDuration時間段內滾動的偏移量,並且判斷滾動是否結束,true代表還沒結束,false則表示滾動介紹了,Scroller類的其他的方法我就不提了,大都是一些get(), set()方法。

看了這麼多,到底要怎麼才能觸發滾動,你心裡肯定有很多疑惑,在說滾動之前我要先提另外一個方法computeScroll(),該方法是滑動的控制方法,在繪製View時,會在draw()過程呼叫該方法。我們先看看computeScroll()的原始碼

  1. /** 
  2.     * Called by a parent to request that a child update its values for mScrollX 
  3.     * and mScrollY if necessary. This will typically be done if the child is 
  4.     * animating a scroll using a {@link android.widget.Scroller Scroller} 
  5.     * object. 
  6.     */
  7.    publicvoid computeScroll() {  
  8.    }  
沒錯,他是一個空的方法,需要子類去重寫該方法來實現邏輯,到底該方法在哪裡被觸發呢。我們繼續看看View的繪製方法draw()
  1. publicvoid draw(Canvas canvas) {  
  2.        finalint privateFlags = mPrivateFlags;  
  3.        finalboolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&  
  4.                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);  
  5.        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;  
  6.        /* 
  7.         * Draw traversal performs several drawing steps which must be executed 
  8.         * in the appropriate order: 
  9.         * 
  10.         *      1. Draw the background 
  11.         *      2. If necessary, save the canvas' layers to prepare for fading 
  12.         *      3. Draw view's content 
  13.         *      4. Draw children 
  14.         *      5. If necessary, draw the fading edges and restore layers 
  15.         *      6. Draw decorations (scrollbars for instance) 
  16.         */
  17.        // Step 1, draw the background, if needed
  18.        int saveCount;  
  19.        if (!dirtyOpaque) {  
  20.            final Drawable background = mBackground;  
  21.            if (background != null) {  
  22.                finalint scrollX = mScrollX;  
  23.                finalint scrollY = mScrollY;  
  24.                if (mBackgroundSizeChanged) {  
  25.                    background.setBounds(00,  mRight - mLeft, mBottom - mTop);  
  26.                    mBackgroundSizeChanged = false;  
  27.                }  
  28.                if ((scrollX | scrollY) == 0) {  
  29.                    background.draw(canvas);  
  30.                } else {  
  31.                    canvas.translate(scrollX, scrollY);  
  32.                    background.draw(canvas);  
  33.                    canvas.translate(-scrollX, -scrollY);  
  34.                }  
  35.            }  
  36.        }  
  37.        // skip step 2 & 5 if possible (common case)
  38.        finalint viewFlags = mViewFlags;  
  39.        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;  
  40.        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;  
  41.        if (!verticalEdges && !horizontalEdges) {  
  42.            // Step 3, draw the content
  43.            if (!dirtyOpaque) onDraw(canvas);  
  44.            // Step 4, draw the children
  45.            dispatchDraw(canvas);  
  46.            // Step 6, draw decorations (scrollbars)
  47.            onDrawScrollBars(canvas);  
  48.            // we're done...
  49.            return;  
  50.        }  
  51.        ......  
  52.        ......  
  53.        ......  

我們只截取了draw()的部分程式碼,這上面11-16行為我們寫出了繪製一個View的幾個步驟,我們看看第四步繪製孩子的時候會觸發dispatchDraw()這個方法,來看看原始碼是什麼內容

  1. /** 
  2.     * Called by draw to draw the child views. This may be overridden 
  3.     * by derived classes to gain control just before its children are drawn 
  4.     * (but after its own view has been drawn). 
  5.     * @param canvas the canvas on which to draw the view 
  6.     */
  7.    protectedvoid dispatchDraw(Canvas canvas) {  
  8.    }  
好吧,又是定義的一個空方法,給子類來重寫的方法,所以我們找到View的子類ViewGroup來看看該方法的具體實現邏輯
  1. @Override
  2. protectedvoid dispatchDraw(Canvas canvas) {  
  3.     finalint count = mChildrenCount;  
  4.     final View[] children = mChildren;  
  5.     int flags = mGroupFlags;  
  6.     if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {  
  7.         finalboolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;  
  8.         finalboolean buildCache = !isHardwareAccelerated();  
  9.         for (int i = 0; i < count; i++) {  
  10.             final View child = children[i];  
  11.             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {  
  12.                 final LayoutParams params = child.getLayoutParams();  
  13.                 attachLayoutAnimationParameters(child, params, i, count);  
  14.                 bindLayoutAnimation(child);  
  15.                 if (cache) {  
  16.                     child.setDrawingCacheEnabled(true);  
  17.                     if (buildCache) {                          
  18.                         child.buildDrawingCache(true);  
  19.                     }  
  20.                 }  
  21.             }  
  22.         }  
  23.         final LayoutAnimationController controller = mLayoutAnimationController;  
  24.         if (controller.willOverlap()) {  
  25.             mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;  
  26.         }  
  27.         controller.start();  
  28.         mGroupFlags &= ~FLAG_RUN_ANIMATION;  
  29.         mGroupFlags &= ~FLAG_ANIMATION_DONE;  
  30.         if (cache) {  
  31.             mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;  
  32.         }  
  33.         if (mAnimationListener != null) {  
  34.             mAnimationListener.onAnimationStart(controller.getAnimation());  
  35.         }  
  36.     }  
  37.     int saveCount = 0;  
  38.     finalboolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;  
  39.     if (clipToPadding) {  
  40.         saveCount = canvas.save();  
  41.         canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,  
  42.                 mScrollX + mRight - mLeft - mPaddingRight,  
  43.                 mScrollY + mBottom - mTop - mPaddingBottom);  
  44.     }  
  45.     // We will draw our child's animation, let's reset the flag
  46.     mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;  
  47.     mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;  
  48.     boolean more = false;  
  49.     finallong drawingTime = getDrawingTime();  
  50.     if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {  
  51.         for (int i = 0; i < count; i++) {  
  52.             final View child = children[i];  
  53.             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {  
  54.                 more |= drawChild(canvas, child, drawingTime);  
  55.             }  
  56.         }  
  57.     } else {  
  58.         for (int i = 0; i < count; i++) {  
  59.             final View child = children[getChildDrawingOrder(count, i)];  
  60.             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {  
  61.                 more |= drawChild(canvas, child, drawingTime);  
  62.             }  
  63.         }  
  64.     }  
  65.     // Draw any disappearing views that have animations
  66.     if (mDisappearingChildren != null) {  
  67.         final ArrayList<View> disappearingChildren = mDisappearingChildren;  
  68.         finalint disappearingCount = disappearingChildren.size() - 1;  
  69.         // Go backwards -- we may delete as animations finish
  70.         for (int i = disappearingCount; i >= 0; i--) {  
  71.             final View child = disappearingChildren.get(i);  
  72.             more |= drawChild(canvas, child, drawingTime);  
  73.         }  
  74.     }  
  75.     if (debugDraw()) {  
  76.         onDebugDraw(canvas);  
  77.     }  
  78.     if (clipToPadding) {  
  79.         canvas.restoreToCount(saveCount);  
  80.     }  
  81.     // mGroupFlags might have been updated by drawChild()
  82.     flags = mGroupFlags;  
  83.     if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {  
  84.         invalidate(true);  
  85.     }  
  86.     if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&  
  87.             mLayoutAnimationController.isDone() && !more) {  
  88.         // We want to erase the drawing cache and notify the listener after the
  89.         // next frame is drawn because one extra invalidate() is caused by
  90.         // drawChild() after the animation is over
  91.         mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;  
  92.         final Runnable end = new Runnable() {  
  93.            publicvoid run() {  
  94.                notifyAnimationListener();  
  95.            }  
  96.         };  
  97.         post(end);  
  98.     }  
  99. }  
這個方法程式碼有點多,但是我們還是挑重點看吧,從65-79行可以看出 在dispatchDraw()裡面會對ViewGroup裡面的子View呼叫drawChild()來進行繪製,接下來我們來看看drawChild()方法的程式碼
  1. protectedboolean drawChild(Canvas canvas, View child, long drawingTime) {  
  2.     ......  
  3.     ......  
  4.     if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&  
  5.                 (child.mPrivateFlags & DRAW_ANIMATION) == 0) {  
  6.             return more;  
  7.         }  
  8.         child.computeScroll();  
  9.         finalint sx = child.mScrollX;  
  10.         finalint sy = child.mScrollY;  
  11.         boolean scalingRequired = false;  
  12.         Bitmap cache = null;  
  13.     ......  
  14.     ......  
  15. }  
只截取了部分程式碼,看到child.computeScroll()你大概明白什麼了吧,轉了老半天終於找到了computeScroll()方法被觸發,就是ViewGroup在分發繪製自己的孩子的時候,會對其子View呼叫computeScroll()方法

整理下思路,來看看View滾動的實現原理,我們先呼叫Scroller的startScroll()方法來進行一些滾動的初始化設定,然後迫使View進行繪製,我們呼叫View的invalidate()或postInvalidate()就可以重新繪製View,繪製View的時候會觸發computeScroll()方法,我們重寫computeScroll(),在computeScroll()裡面先呼叫Scroller的computeScrollOffset()方法來判斷滾動有沒有結束,如果滾動沒有結束我們就呼叫scrollTo()方法來進行滾動,該scrollTo()方法雖然會重新繪製View,但是我們還是要手動呼叫下invalidate()或者postInvalidate()來觸發介面重繪,重新繪製View又觸發computeScroll(),所以就進入一個迴圈階段,這樣子就實現了在某個時間段裡面滾動某段距離的一個平滑的滾動效果
也許有人會問,幹嘛還要呼叫來呼叫去最後在呼叫scrollTo()方法,還不如直接呼叫scrollTo()方法來實現滾動,其實直接呼叫是可以,只不過scrollTo()是瞬間滾動的,給人的使用者體驗不太好,所以Android提供了Scroller類實現平滑滾動的效果。為了方面大家理解,我畫了一個簡單的呼叫示意圖


好了,講到這裡就已經講完了Scroller類的滾動實現原理啦,不知道大家理解了沒有,Scroller能實現很多滾動的效果,由於考慮到這篇文章的篇幅有點長,所以這篇文章就不帶領大家來使用Scroller類了,我將在下一篇文章將會帶來Scroller類的使用,希望大家到時候關注下,有疑問的同學在下面留言,我會為大家解答的!


相關推薦

Android 原始碼角度解析Scroller滾動實現原理

今天給大家講解的是Scroller類的滾動實現原理,可能很多朋友不太瞭解該類是用來幹嘛的,但是研究Launcher的朋友應該對他很熟悉,Scroller類是滾動的一個封裝類,可以實現View的平滑滾動效果,什麼是實現View的平滑滾動效果呢,舉個簡單的例子,一個View

原始碼角度分析ViewGroup中事件分發流程

序言 這篇博文不是對事件分發機制全面的介紹,只是從原始碼的角度分析ACTION_DOWN、ACTION_MOVE、ACTION_UP事件在ViewGroup中的分發邏輯,瞭解各個事件在ViewGroup的分發邏輯對理解、解決滑動衝突問題很有幫助。 ViewGroup中事件分發流

Android事件分發機制完全解析原始碼角度徹底理解(上)-郭霖

其實我一直準備寫一篇關於Android事件分發機制的文章,從我的第一篇部落格開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如:onTouch和onTouchEvent有什麼區別,又該如何使用?為什麼給ListView引入了一

Android事件分發機制完全解析原始碼角度徹底理解(下)-郭霖

記得在前面的文章中,我帶大家一起從原始碼的角度分析了Android中View的事件分發機制,相信閱讀過的朋友對View的事件分發已經有比較深刻的理解了。 還未閱讀過的朋友,請先參考 Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(上) 。 那麼今天我們將繼

Android Volley完全解析 四 ,原始碼角度理解Volley

                經過前三篇文章的學習,Volley的用法我們已經掌握的差不多了,但是對於Volley的工作原理,恐怕有很多朋友還不是很清楚。因此,本篇文章中我們就來一起閱讀一下Volley的原始碼,將它的工作流程整體地梳理一遍。同時,這也是Volley系列的最後一篇文章了。其實,Volley的

Android ListView工作原理完全解析原始碼角度徹底理解

在Android所有常用的原生控制元件當中,用法最複雜的應該就是ListView了,它專門用於處理那種內容元素很多,手機螢幕無法展示出所有內容的情況。ListView可以使用列表的形式來展示內容,超出螢幕部分的內容只需要通過手指滑動就可以移動到螢幕內了。另外ListView還

Android事件分發機制完全解析原始碼角度徹底理解(上)

其實我一直準備寫一篇關於Android事件分發機制的文章,從我的第一篇部落格開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如:onTouch和onTouchEvent有什麼區別,又該如何使用?為什麼給ListView引入

Android事件分發機制完全解析原始碼角度徹底理解(下)

記得在前面的文章中,我帶大家一起從原始碼的角度分析了Android中View的事件分發機制,相信閱讀過的朋友對View的事件分發已經有比較深刻的理解了。那麼今天我們將繼續上次未完成的話題,從原始碼的角度分析ViewGroup的事件分發。首先我們來探討一下,什麼是ViewGro

Android事件分發機制完全解析原始碼角度徹底理解(下) (出自郭林老師)

記得在前面的文章中,我帶大家一起從原始碼的角度分析了Android中View的事件分發機制,相信閱讀過的朋友對View的事件分發已經有比較深刻的理解了。 那麼今天我們將繼續上次未完成的話題,從原始碼的角度分析ViewGroup的事件分發。 首先我們來探討一下,什

Android事件分發機制完全解析原始碼角度徹底理解(上) (出自郭霖老師)

其實我一直準備寫一篇關於Android事件分發機制的文章,從我的第一篇部落格開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如:onTouch和onTouchEvent有什麼區別,又該如何使用?為什麼給ListView引入了

Android AsyncTask完全解析原始碼角度徹底理解

我們都知道,Android UI是執行緒不安全的,如果想要在子執行緒裡進行UI操作,就需要藉助Android的非同步訊息處理機制。之前我也寫過了一篇文章從原始碼層面分析了Android的非同步訊息處理機制,感興趣的朋友可以參考 Android Handler、Message完

Android原始碼解析Bitmap佔用記憶體正確的計算公式

Bitmap 前言 Bitmap簡介 畫素儲存方式 圖片壓縮格式 Bitmap記憶體計算 釋放記憶體 獲取Bitmap所佔記憶體 計算所佔記憶體 舉例 Bitmap.getAllo

【安卓筆記】Volley全方位解析,原始碼角度徹底理解

轉載請宣告原出處(blog.csdn.net/chdjj),謝謝! -------------------------- 參考資料 1.http://www.codekk.com/open-source-project-analysis/detail/Android/g

原始碼角度解析 - ScrollView巢狀ListView只顯示一行的問題

<ScrollView android:id="@+id/scroll_view" android:layout_width="match_parent" android:layout_height="match_parent">

原始碼角度解析 - ScrollView巢狀ViewPager不顯示的問題

<ScrollView android:id="@+id/scroll_view" android:layout_width="match_parent" android:layout_height="match_parent">

手寫Struts,深入原始碼中心解析

個人剖析,不喜勿噴 掃碼關注公眾號,不定期更新干活 在此申明本博文並非原創,原文:http://blog.csdn.net/lenotang/article/details/3336623,本文章是在此文章基礎上進行優化。也談不上優化,只是加上了點自己的想法 jar包準備 為什麼會用到這兩個j

原始碼角度分析ViewStub 疑問與原理

一、提出疑問     ViewStub比較簡單,之前文章都提及到《Android 效能優化 三 佈局優化ViewStub標籤的使用》,但是在使用過程中有一個疑惑,到底是ViewStub上設定的引數有效還是在其包括的layout中設定引數有效?如果不明白描述的問題,可以看下以下佈局虛擬碼。 res/lay

二次元的角度看建築「摩根·帝巢丨湛江工程資質×××」

特色 ado 日本 停止 就是 設定 ima gdc dba 我從小到大是個十足的動漫迷,從小學開始迷戀日本漫畫直至現在。從《哆啦A夢》、《櫻桃小丸子》這批經典到後來的《海賊王》、《火影忍者》這批灌輸熱血的雞湯……但如今似乎更流行二次元動漫。 二次元動漫不僅盛產帥哥美女,還

[jvm解析系列][十四]動態代理和裝飾模式,原始碼深入理解裝飾模式和動態代理的區別。

不知道大家知不知道設計模式中有一種叫做裝飾,舉一個簡單的例子。 一天一個年輕領導小王講話:咳咳,我們一定要xxx抓緊xxxx學習xxx的精神!好,今天的會議結束! 然後有一個老領導李同志接過來說:那個我在補充兩點,個別同志xxx,一定要注意xxx。好散會。 然後另一天小王同

Android解析ScrollView--仿QQ空間標題欄漸變

緒論 今天來研究的是ScrollView-滾動檢視,滾動檢視又分橫向滾動檢視(HorizontalScrollView)和縱向滾動檢視(ScrollView),今天主要研究縱向的。相信大家在開發中經常用到,ScrollView的功能已經很強大了,但是仍然滿足不