Android自定义ViewAndroid View 基础

Android高手秘笈之View的绘制你应该知道的一切

2019-04-07  本文已影响7人  门心叼龙

目录
[1. 简述android的UI系统的层级关系?]
[2. View绘制的整体流程?]
[3. MeasureSpec是什么?]
[4. 简述measure的测量流程?]
[5. 简述view的布局流程?]
[6. 简述onDraw绘制的基本流程?]
[7. View绘制简易流程?]
[8. 怎么在view绘制之前获取控件的宽和高? ]


1. 简述android的UI系统的层级关系?

PhoneWindow是Android系统中最基本的系统窗口,每个Activity都会创建一个,PhoneWindow是Activity和View系统交互的接口,DecorView本质上是一个FrameLayout,是所有Activity中所有View的祖先

image image.gif

2. View绘制的整体流程?

当一个应用启动时,会启动一个主Activity,Android系统会根据Activity的布局来对它进行绘制,绘制会从视图VewRoot的performTraversals()方法开始,从上到下遍历整个视图树,每个View控件负责绘制自己,而ViewGroup还需要通知自己的子View进行绘制操作。整个绘制过程分为三个步骤,分别是测量(Measure),布局(Layout)和绘制(Draw)。

private void performTraversals() {
     int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
     int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
}

image.gif
//执行测量流程
performMeasure(childWidchMeasureSpec,childHeightMeasureSpec);
image.gif
//执行布局流程
performLayout(lp,desiredWindowWidth,desiredWindowHeight);
image.gif
//执行绘制流程
performDraw();
image.gif image image.gif

3. MeasureSpec是什么?

MeasureSpec表示的是一个32位的整型值,它的高2位表示测量模式SpecMode,低30位表示某种测量模式下的规格大小SpecSize。MeasureSpec是View类的一个静态内部类,用来说明应该如何测量这个View
测量模式有三种:

4.简述measure的测量流程?

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
}
image.gif
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
        final int size = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < size; ++i) {
            final View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }
image.gif
protected void measureChild(View child, int parentWidthMeasureSpec,
            int parentHeightMeasureSpec) {
        final LayoutParams lp = child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
image.gif
  public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
              ...
                // measure ourselves, this should set the measured dimension flag back
                onMeasure(widthMeasureSpec, heightMeasureSpec);
               ...
        }
image.gif
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
image.gif

5. 简述view的布局流程?

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {
                    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()))
    }
image.gif
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;
    }
image.gif
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}
image.gif

6. 简述onDraw绘制的基本流程?

private void performDraw(){
...
draw(fullRedrawNeeded);
...
}
image.gif
private void draw(boolean fullRedrawNeeded) {
...
 if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                    return;
                }
...
}
image.gif
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {

        return true;
    }
image.gif
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;

        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1\. Draw the background
         *      2\. If necessary, save the canvas' layers to prepare for fading
         *      3\. Draw view's content
         *      4\. Draw children
         *      5\. If necessary, draw the fading edges and restore layers
         *      6\. Draw decorations (scrollbars for instance)
         */

        // Step 1, draw the background, if needed

        // Step 6, draw decorations (foreground, scrollbars)
        onDrawForeground(canvas);
    }
image.gif

7. View绘制简易流程?

image image.gif

image image.gif

8.怎么在view绘制之前获取控件的宽和高?

ViewTreeObserver vto = imageView.getViewTreeObserver();
        vto.addOnPreDrawListener(newViewTreeObserver.OnPreDrawListener(){
publicboolean onPreDraw(){
              imageView.getViewTreeObserver().removeOnPreDrawListener(this)
               intheight = imageView.getMeasuredHeight();
               intwidth = imageView.getMeasuredWidth();
                textView.append("\n"+height+","+width);
               returntrue;
           }
       });
image.gif
ViewTreeObserver vto2 = imageView.getViewTreeObserver(); 
    vto2.addOnGlobalLayoutListener(newOnGlobalLayoutListener(){
    @Override 
    publicvoidonGlobalLayout(){
        imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
        textView.append("\n\n"+imageView.getHeight()+","+imageView.getWidth());
     } 
 });  
image.gif
上一篇下一篇

猜你喜欢

热点阅读