<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      安卓筆記俠

      專注安卓開發

      導航

      Android View體系(八)從源碼解析View的layout和draw流程

      前言

      上一篇文章我們講了View的measure的流程,接下來我們講下View的layout和draw流程,如果你理解了View的measure的流程,那這篇文章自然就不在話下了。

      1.View的layout流程

      先來看看View的layout()方法:

      public void layout(int l, int t, int r, int b) {
             if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
                 onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
                 mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
             }
             int oldL = mLeft;
             int oldT = mTop;
             int oldB = mBottom;
             int oldR = mRight;
             boolean changed = isLayoutModeOptical(mParent) ?
                     setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
             if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
                 onLayout(changed, l, t, r, b);
                 mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
                 ListenerInfo li = mListenerInfo;
                 if (li != null && li.mOnLayoutChangeListeners != null) {
                     ArrayList<OnLayoutChangeListener> listenersCopy =
                             (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
                     int numListeners = listenersCopy.size();
                     for (int i = 0; i < numListeners; ++i) {
                         listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
                     }
                 }
             }
             mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
             mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
         }
      View Code

      傳進來里面的四個參數分別是View的四個點的坐標,它的坐標不是相對屏幕的原點,而且相對于它的父布局來說的。 l 和 t 是子控件左邊緣和上邊緣相對于父類控件左邊緣和上邊緣的距離;
      r 和 b是子控件右邊緣和下邊緣相對于父類控件左邊緣和上邊緣的距離。來看看setFrame()方法里寫了什么:

      protected boolean setFrame(int left, int top, int right, int bottom) {
          boolean changed = false;
          if (DBG) {
              Log.d("View", this + " View.setFrame(" + left + "," + top + ","
                      + right + "," + bottom + ")");
          }
          if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
              changed = true;
              // Remember our drawn bit
              int drawn = mPrivateFlags & PFLAG_DRAWN;
              int oldWidth = mRight - mLeft;
              int oldHeight = mBottom - mTop;
              int newWidth = right - left;
              int newHeight = bottom - top;
              boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
              // Invalidate our old position
              invalidate(sizeChanged);
              mLeft = left;
              mTop = top;
              mRight = right;
              mBottom = bottom;
              mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
          ...省略  
          }
          return changed;
      }
      View Code

      在setFrame()方法里主要是用來設置View的四個頂點的值,也就是mLeft 、mTop、mRight和 mBottom的值。在調用setFrame()方法后,調用onLayout()方法:

      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        }
      View Code

      onLayout()方法沒有去做什么,這個和onMeasure()方法類似,確定位置時根據不同的控件有不同的實現,所以在View和ViewGroup中均沒有實現onLayout()方法。既然這樣,我們就來看看LinearLayout的onLayout()方法:

      @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            if (mOrientation == VERTICAL) {
                layoutVertical(l, t, r, b);
            } else {
                layoutHorizontal(l, t, r, b);
            }
        }
      View Code

      layoutVertical做了什么呢?

      void layoutVertical(int left, int top, int right, int bottom) {
             final int paddingLeft = mPaddingLeft;
             int childTop;
             int childLeft;       
             // Where right end of child should go
             final int width = right - left;
             int childRight = width - mPaddingRight;     
             // Space available for child
             int childSpace = width - paddingLeft - mPaddingRight;    
             final int count = getVirtualChildCount();
             final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
             final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
             switch (majorGravity) {
                case Gravity.BOTTOM:
                    // mTotalLength contains the padding already
                    childTop = mPaddingTop + bottom - top - mTotalLength;
                    break;
                    // mTotalLength contains the padding already
                case Gravity.CENTER_VERTICAL:
                    childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
                    break;
                case Gravity.TOP:
                default:
                    childTop = mPaddingTop;
                    break;
             }
             for (int i = 0; i < count; i++) {
                 final View child = getVirtualChildAt(i);
                 if (child == null) {
                     childTop += measureNullChild(i);
                 } else if (child.getVisibility() != GONE) {
                     final int childWidth = child.getMeasuredWidth();
                     final int childHeight = child.getMeasuredHeight();            
                     final LinearLayout.LayoutParams lp =
                             (LinearLayout.LayoutParams) child.getLayoutParams();           
                     int gravity = lp.gravity;
                     if (gravity < 0) {
                         gravity = minorGravity;
                     }
                     final int layoutDirection = getLayoutDirection();
                     final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
                     switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                         case Gravity.CENTER_HORIZONTAL:
                             childLeft = paddingLeft + ((childSpace - childWidth) / 2)
                                     + lp.leftMargin - lp.rightMargin;
                             break;
                         case Gravity.RIGHT:
                             childLeft = childRight - childWidth - lp.rightMargin;
                             break;
                         case Gravity.LEFT:
                         default:
                             childLeft = paddingLeft + lp.leftMargin;
                             break;
                     }
                     if (hasDividerBeforeChildAt(i)) {
                         childTop += mDividerHeight;
                     }
                     childTop += lp.topMargin;
                     setChildFrame(child, childLeft, childTop + getLocationOffset(child),
                             childWidth, childHeight);
                     childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
                     i += getChildrenSkipCount(child, i);
                 }
             }
         }
      View Code

      這個方法會遍歷子元素并調用setChildFrame()方法:

      private void setChildFrame(View child, int left, int top, int width, int height) {        
            child.layout(left, top, left + width, top + height);
      }
      View Code

      在setChildFrame()方法中調用子元素的layout()方法來確定自己的位置。我們看到childTop這個值是逐漸增大的,這是為了在垂直方向,子元素是一個接一個排列的而不是重疊的。

      2.View的draw流程

      View的draw流程很簡單,先來看看View的draw()方法:

       public void draw(Canvas canvas) {
              final int privateFlags = mPrivateFlags;
              final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                      (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
              mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;   
              // Step 1, draw the background, if needed
              int saveCount;
              if (!dirtyOpaque) {
                  drawBackground(canvas);
              }
      ...
         // Step 2, save the canvas' layers
              int paddingLeft = mPaddingLeft;
              final boolean offsetRequired = isPaddingOffsetRequired();
              if (offsetRequired) {
                  paddingLeft += getLeftPaddingOffset();
              }
      ...
        // Step 3, draw the content
              if (!dirtyOpaque) onDraw(canvas);
              // Step 4, draw the children
              dispatchDraw(canvas);
      ...
         // Step 5, draw the fade effect and restore layers
              final Paint p = scrollabilityCache.paint;
              final Matrix matrix = scrollabilityCache.matrix;
              final Shader fade = scrollabilityCache.shader;
      ...
        // Step 6, draw decorations (scrollbars)
              onDrawScrollBars(canvas);
              if (mOverlay != null && !mOverlay.isEmpty()) {
                  mOverlay.getOverlayView().dispatchDraw(canvas);
              }
         }
      View Code

      從源碼的注釋我們看到draw流程有六個步驟,其中第2步和第5步可以跳過:

      1. 如果有設置背景,則繪制背景
      2. 保存canvas層
      3. 繪制自身內容
      4. 如果有子元素則繪制子元素
      5. 繪制效果
      6. 繪制裝飾品(scrollbars)

      好了,關于View的工作流程就講到這里了,接下來會講到自定義View。

      posted on 2016-11-06 19:52  安卓筆記俠  閱讀(931)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 亚洲国产婷婷综合在线精品| 一区二区中文字幕久久| brazzers欧美巨大| 97欧美精品系列一区二区| 国产精欧美一区二区三区| 日韩一卡二卡三卡四卡五卡 | 内地偷拍一区二区三区| 99精品国产一区二区三区不卡 | 亚洲国产日韩欧美一区二区三区 | 成人精品大片—懂色av| 国产精品爱久久久久久久| 亚洲男人第一无码av网站| 亚洲 欧美 唯美 国产 伦 综合| 久久精品A一国产成人免费网站| 辽宁省| 亚洲欧美成人综合久久久| 免费AV片在线观看网址| 亚洲 丝袜 另类 校园 欧美| 亚洲成人一区二区av| 亚洲欧美日韩综合久久久| 麻豆精品一区二正一三区| av无码精品一区二区三区| av深夜免费在线观看| 巨大黑人极品videos精品| 自拍偷亚洲产在线观看| 国产99视频精品免费视频36| 无码h黄肉动漫在线观看| 国产日韩入口一区二区| 噜噜久久噜噜久久鬼88| 龙泉市| 欧美性xxxxx极品| 荡乳尤物h| 久久综合97丁香色香蕉| 久久国产精品福利一区二区三区| 亚洲第一国产综合| 日本久久精品一区二区三区| 国产精品综合av一区二区国产馆| 亚洲国产日韩a在线播放| 丰满的少妇一区二区三区| 国产成人高清亚洲综合| 亚洲天堂av在线免费看|