美文网首页Android
Android绘制源码分析(下)

Android绘制源码分析(下)

作者: 大佬的上半生 | 来源:发表于2019-11-20 16:32 被阅读0次

    Android绘制源码分析上介绍了这个屏幕的架构,以及相关的系统类Android绘制源码分析中介绍了整个绘制的启动,绘制过程 ,绘制 完毕的一个流程,这篇的终点就是介绍我们平常自定的三步骤

    image.png

    View的生命周期

    image.png

    1.创建Activity时View的状态

    image.png

    2.退出Activity时View的状态

    image.png

    3.按住Home键时View的状态

    image.png
    生命周期介绍
    1. onFinishInflate() 当View中所有的子控件均被映射成xml后触发
    2. onMeasure( int , int ) 确定所有子元素的大小
    3. onLayout( boolean , int , int , int , int ) 当View分配所有的子元素的大小和位置时触发
    4. onSizeChanged( int , int , int , int ) 当view的大小发生变化时触发
    5. onDraw(Canvas) view渲染内容的细节
    6. onKeyDown( int , KeyEvent) 有按键按下后触发
    7. onKeyUp( int , KeyEvent) 有按键按下后弹起时触发
    8. onTrackballEvent(MotionEvent) 轨迹球事件
    9. onTouchEvent(MotionEvent) 触屏事件
    10. onFocusChanged( boolean , int , Rect) 当View获取或失去焦点时触发
    11. onWindowFocusChanged( boolean ) 当窗口包含的view获取或失去焦点时触发
    12. onAttachedToWindow() 当view被附着到一个窗口时触发
    13. onDetachedFromWindow() 当view离开附着的窗口时触发,Android123提示该方法和 onAttachedToWindow() 是相反的。
    14. onWindowVisibilityChanged( int ) 当窗口中包含的可见的view发生变化时触发

    1.View确定大小

    如何确定大小?肯定就需要测量,从根 View 递归调用每一级子 View 的 measure() 方法,对它们进行测量。
    所以测量是指父View对子View测量,父View收到子View宽高参数,然后结合自身的宽高,一起商量测量出给宽高传递给子View

    • 测量情况一
    ViewRootImpl
    
     int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
     int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
       private static int getRootMeasureSpec(int windowSize, int rootDimension) {
            int measureSpec;
            switch (rootDimension) {
    
            case ViewGroup.LayoutParams.MATCH_PARENT:
                // Window can't resize. Force root view to be windowSize.
                measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
                break;
            case ViewGroup.LayoutParams.WRAP_CONTENT:
                // Window can resize. Set max size for root view.
                measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
                break;
            default:
                // Window wants to be an exact size. Force root view to be that size.
                measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
                break;
            }
            return measureSpec;
        }
    
    • 测量情况二
    ViewRootImpl
    
    if (lp.horizontalWeight > 0.0f) {
                            width += (int) ((mWidth - width) * lp.horizontalWeight);
                            childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
                                    MeasureSpec.EXACTLY);
                            measureAgain = true;
                        }
                        if (lp.verticalWeight > 0.0f) {
                            height += (int) ((mHeight - height) * lp.verticalWeight);
                            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
                                    MeasureSpec.EXACTLY);
                            measureAgain = true;
                        }
    
        public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
                 @MeasureSpecMode int mode) {
                if (sUseBrokenMakeMeasureSpec) {
                    return size + mode;
                } else {
                    return (size & ~MODE_MASK) | (mode & MODE_MASK);
                }
            }
    
    • 传递给子View mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
     public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
            boolean optical = isLayoutModeOptical(this);
            if (optical != isLayoutModeOptical(mParent)) {
                Insets insets = getOpticalInsets();
                int oWidth  = insets.left + insets.right;
                int oHeight = insets.top  + insets.bottom;
                widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);
                heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
            }
            if (forceLayout || needsLayout) {
                // first clears the measured dimension flag
                mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
                resolveRtlPropertiesIfNeeded();
                int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
                if (cacheIndex < 0 || sIgnoreMeasureCache) {
                    // measure ourselves, this should set the measured dimension flag back
                    onMeasure(widthMeasureSpec, heightMeasureSpec);
                    mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
                } else {
                    long value = mMeasureCache.valueAt(cacheIndex);
                    // Casting a long to int drops the high 32 bits, no mask needed
                    setMeasuredDimensionRaw((int) (value >> 32), (int) value);
                    mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
                }
            }
            mOldWidthMeasureSpec = widthMeasureSpec;
            mOldHeightMeasureSpec = heightMeasureSpec;
        }
      
    
    • 通过onMeasure父view告诉子View的宽高和测量的模式
       protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
        }
    
    • 子类拿到父类给的高度后,可能需要重新要求高度,但是高度是由父类共同决定,所以需要校验自己设定的高度合不合理
      public static 
    int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
            final int specMode = MeasureSpec.getMode(measureSpec);
            final int specSize = MeasureSpec.getSize(measureSpec);
            final int result;
            switch (specMode) {
                case MeasureSpec.AT_MOST:
                    if (specSize < size) {
                        result = specSize | MEASURED_STATE_TOO_SMALL;
                    } else {
                        result = size;
                    }
                    break;
                case MeasureSpec.EXACTLY:
                    result = specSize;
                    break;
                case MeasureSpec.UNSPECIFIED:
                default:
                    result = size;
            }
            return result | (childMeasuredState & MEASURED_STATE_MASK);
        }
    

    2.确定子View位置onLayout()

      performLayout(lp, mWidth, mHeight);
    
     private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
                int desiredWindowHeight) {
     final View host = mView;
            try {
                host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
                  ........................................................
    }
    }
    

    host 就是DecorView,host.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);
                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);
                    }
                }
            }
        }
    

    setFrame(l, t, r, b)这个方法用于确认View四个点的位置,及初始化left,top,right,bottom的位置,这是ViewGroup布局完成

    protected boolean setFrame(int left, int top, int right, int bottom) {
            boolean changed = false;
    
            if (DBG) {
                Log.d(VIEW_LOG_TAG, 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);
    
                mPrivateFlags |= PFLAG_HAS_BOUNDS;
    
    
                if (sizeChanged) {
                    sizeChange(newWidth, newHeight, oldWidth, oldHeight);
                }
    
                if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) {
                    // If we are visible, force the DRAWN bit to on so that
                    // this invalidate will go through (at least to our parent).
                    // This is because someone may have invalidated this view
                    // before this call to setFrame came in, thereby clearing
                    // the DRAWN bit.
                    mPrivateFlags |= PFLAG_DRAWN;
                    invalidate(sizeChanged);
                    // parent display list may need to be recreated based on a change in the bounds
                    // of any child
                    invalidateParentCaches();
                }
            }
            return changed;
        }
    
    
    1. mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);原来这个就是设置View位置的最终方法,点进去就是native代码了

    2. sizeChange(newWidth, newHeight, oldWidth, oldHeight);这个就是回调给子类确定最终宽高的onSizeChanged(newWidth, newHeight, oldWidth, oldHeight)

    以上就是确定View位置的大概方法和流程

    3.开始绘制ViewRootlmpl->draw

    performDraw()
    
      private void performDraw() {
            if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
                return;
            } else if (mView == null) {
                return;
            }
       //TODO 省略
            try {
                boolean canUseAsync = draw(fullRedrawNeeded);
                if (usingAsyncReport && !canUseAsync) {
                    mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
                    usingAsyncReport = false;
                }
            } finally {
                mIsDrawing = false;
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
    
             //TODO 省略
    

    ViewRootlmpl->boolean canUseAsync = draw(fullRedrawNeeded);

     private boolean draw(boolean fullRedrawNeeded) {
            Surface surface = mSurface;
            if (!surface.isValid()) {
                return false;
            }
                     //TODO 省略
    
                    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
                            scalingRequired, dirty, surfaceInsets)) {
                        return false;
                    }
                }
            }
       //TODO 省略
       
        }
    

    ViewRootlmpl->drawSoftware()

      private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
                boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
    
            // Draw with software renderer.
            final Canvas canvas;
            try {
                canvas = mSurface.lockCanvas(dirty);
            try {
                if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
                    canvas.drawColor(0, PorterDuff.Mode.CLEAR);
                }
                dirty.setEmpty();
                mIsAnimating = false;
                mView.mPrivateFlags |= View.PFLAG_DRAWN;
                try {
                    mView.draw(canvas);
                    drawAccessibilityFocusedDrawableIfNeeded(canvas);
                } 
            } 
            return true;
        }
    

    canvas 是由图表Surface 赋值传值给 mView.draw(canvas);
    View->draw()

     public void draw(Canvas canvas) {
    
            if (!dirtyOpaque) {
                drawBackground(canvas);
            }
            if (!verticalEdges && !horizontalEdges) {
                // Step 3, draw the content
                if (!dirtyOpaque) onDraw(canvas);
    
                // Step 4, draw the children
                dispatchDraw(canvas);
    
                drawAutofilledHighlight(canvas);
    
                // Overlay is part of the content and draws beneath Foreground
                if (mOverlay != null && !mOverlay.isEmpty()) {
                    mOverlay.getOverlayView().dispatchDraw(canvas);
                }
    
                // Step 6, draw decorations (foreground, scrollbars)
                onDrawForeground(canvas);
    
                // Step 7, draw the default focus highlight
                drawDefaultFocusHighlight(canvas);
            int solidColor = getSolidColor();
            if (solidColor == 0) {
                if (drawTop) {
                    canvas.saveUnclippedLayer(left, top, right, top + length);
                }
    
                if (drawBottom) {
                    canvas.saveUnclippedLayer(left, bottom - length, right, bottom);
                }
    
                if (drawLeft) {
                    canvas.saveUnclippedLayer(left, top, left + length, bottom);
                }
    
                if (drawRight) {
                    canvas.saveUnclippedLayer(right - length, top, right, bottom);
                }
            } else {
                scrollabilityCache.setFadeColor(solidColor);
            }
    
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);
    
            // Step 4, draw the children
            dispatchDraw(canvas);
            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);
      
        }
    

    最后由子类在onDraw()自定义绘制

    总结一下自定义控件吧,自定义控件分几种

    1.单独一个子控件,我的目的就是绘制,所以重点就是draw里面的逻辑,不需要去管layout,或者measure
    2.自定义时,里面包含多个控件,这时需要去测量 (measure),布局(layout),绘制(draw)
    3.自定义时明确功能,可能只是需要在TextView中加一个功能,所以可以直接继承TextView
    3.自定义时可能是父布局里,如RelativeLayout,只是需要改变某个需求,可以直接继承RelativeLayout
    4.自定义组合控件,某个布局块的一部分控件采用父控件统一管理

    相关文章

      网友评论

        本文标题:Android绘制源码分析(下)

        本文链接:https://www.haomeiwen.com/subject/disjictx.html