详细分析View的绘制过程

SimNow模拟环境实战:用vnpy_ctp连接期货市场的5个关键配置步骤 本文提供了一份详细的量化交易实战指南,重点讲解如何使用vnpy_ctp插件连接SimNow期货模拟环境的5个关键配置步骤。内容涵盖SimNow环境选择、vnpy_ctp核心参数解析、实战连接脚本编写、深度排错技巧以及向实盘迁移的准备,旨在帮助量化交易开发者快速搭建可靠的测试环境,避免常见陷阱。 阅读详情

转载注明出处:http://blog.youkuaiyun.com/xiaohanluo/article/details/52084105

1. View简介

       View类是Android中各种交互式组件的基类,显示在屏幕上的视图由一个或多个View组成,它们排列在一个View树中,我们可以向树中添加新的View或者View树。



2. View绘制过程

       每一个Android研发每天都会和各种View打交道,Android中任何一个布局或者任何一个控件都是直接或者间接继承View的,例如TextView、Button、ListView等等,尽管这些布局或者控件都是系统提供好的我们直接用就行,但是如果我们知道了这些布局或者控件绘制到屏幕上的过程后,我们就可以根据我们的需求自定义View了。 
       任何一个控件能够精确的显示到屏幕上,都是经过严格的绘制过程的。每一个View的绘制过程都会经历三个步骤,即Measure、Layout、Draw。

绘制过程 功能
Measure 计算视图的大小
Layout 计算视图的位置
Draw 绘制视图

注:视图可为单个控件也可以为布局

       整个View树的绘制过程是从ViewRootImpl类的performsTraversals()方法开始的,大体绘制过程就是根据之前设置的状态,选择是否需要重新计算视图大小、重新计算视图位置、重新绘制视图。

       ViewRootImpl是一个隐藏类,需要下载源码后查看,在用户android_sdk目录/sources/android-版本号/android/view/目录下可以找到,有兴趣同学可以自己去查看。

视图的绘制过程如下图。

图-2 绘制过程流程图

2.1 Measure

Measure是测量的意思,在这个过程中主要计算视图的大小。

在这个过程主要有三个主要的方法。

  • public final void measure(int widthMeasureSpec, int heightMeasureSpec)
  • protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
  • protected final void setMeasuredDimension(int measuredWidth, int measureHight)

       View类中的measure()方法是final类型,它是不可被子类重写的,在measure()方法中主要是一些逻辑的判断,主要的计算过程是在onMeasure()方法中,这个方法可以被子类覆盖。我们先看看一下measure()的这个方法的部分代码。

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    ...
    int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
            mMeasureCache.indexOfKey(key);
    if (cacheIndex < 0 || sIgnoreMeasureCache) {
        // 计算视图大小
        onMeasure(widthMeasureSpec, heightMeasureSpec);
        mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    } else {
        ...
    }
    ...
    mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;

    mOldWidthMeasureSpec = widthMeasureSpec;
    mOldHeightMeasureSpec = heightMeasureSpec;

    ...
}
        我们可以看到如果强制计算或者忽略之前的计算结果,都会调用 onMeasure() 方法。我们看一下 onMeasure() 方法,里面调用了 getDefaultSize() 方法,一并查看一下。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
        getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    // 获取视图模式和大小
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    // 根据计算模式,得到计算的视图大小
    switch (specMode) {
    case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}

   onMeasure()这个方法有两个参数,是父视图调用子视图measure()方法对子视图进行测量时候,传入的参数,通过这两个参数,分别确定子视图宽度和高度的specSize和specMode。MeasureSpec参数的值为int型,32位,因为specMode分三种,所以前两位是用来标识specMode的,specMode三种模式如下。

  • MeasureSpec.UNSPECIFIED 父视图没有添加任何限制,子视图大小可以使任意大小。
  • MeasureSpec.AT_MOST 父视图为子视图指定了大小,即specSize
  • MeasureSpec.EXACTLY 子视图大小最多是俯视图指定的代销,即specSize

       在getDefaultSize方法中,我们可以看到如果是UNSPECIFIED模式,返回的大小是系统建议的最小宽度或者最小高度;如果是AT_MOST或者EXACTLY模式,返回的是从measureSpec参数里面获取的specSize。

       所以我们可以得知,其实计算视图的大小是在onMeasure()方法里进行的,而且这个方法是可以重写的。在获取了视图的大小之后,调用setMeasuredDimension将计算后的数据储存起来。

       单个View大小计算比较简单,大体跟上面相似,但是如果是ViewGroup的话,它的计算过程是怎么样的呢。因为ViewGroup是个抽象的类,那我们就以LinearLayout为例子查看具体的细节,直接来查看一下LinearLayout的onMeasure的过程。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mOrientation == VERTICAL) {
        measureVertical(widthMeasureSpec, heightMeasureSpec);
    } else {
        measureHorizontal(widthMeasureSpec, heightMeasureSpec);
    }
}

       代码很简单,先查看了LinearLayout方向 orientation 属性,根据不同的属性进行不同的计算,我们只挑垂直方向时候的计算过程查看吧,去看一下 measureVertical() 方法,方法代码非常多,我们只查看重要的逻辑。

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
    // 初始化一些局部变量
    ...
    // 获取垂直方向上的子View个数
    final int count = getVirtualChildCount();

    // 获取本身的specSize和specMode
    final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    // 初始化局部变量
    ...

    // 遍历子View获取其高度,并记录下子View中最高的高度数值
    for (int i = 0; i < count; ++i) {
        final View child = getVirtualChildAt(i);

        // 子View为空,measureNullChile(int)返回值恒为0
        if (child == null) {
            mTotalLength += measureNullChild(i);
            continue;
        }

        // 子View不可见,直接跳过该View的measure过程,getChildrenSkipCount()返回值恒为0
        // 注意如果view的可见属性设置为VIEW.INVISIBLE,还是会计算该view大小
        if (child.getVisibility() == View.GONE) {
           i += getChildrenSkipCount(child, i);
           continue;
        }

        // 添加分割线高度
        if (hasDividerBeforeChildAt(i)) {
            mTotalLength += mDividerHeight;
        }

        // 获取子View的布局参数
        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

        // 记录子View是否有weight属性设置,用于后面判断是否需要二次measure
        totalWeight += lp.weight;

        if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
            // 此地方可以简单理解为
            // 如果LinearLayout的specMode为EXACTLY且子View设置了weight属性,在这里会跳过子View的measure过程
            // 同时标记skippedMeasure属性为true,后面会根据该属性决定是否进行第二次measure
            // 这也是为什么LinearLayout的子View使用weight属性时候,最好替换成RelativeLayout布局
            // 因为如果LinearLayout的子View设置了weight,会进行两次measure计算,比较耗时
            final int totalLength = mTotalLength;
            mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
            skippedMeasure = true;
        } else {
            int oldHeight = Integer.MIN_VALUE;

            if (lp.height == 0 && lp.weight > 0) {
                // 如果LinearLayout本身的specMode为UNSPECIFIED或者AT_MOST时候
                // 子View设置了weight属性想占用一些空间,暂时将该子view的高度属性设置为WRAP_CONTENT
                // 至此,不论LinearLayout何种specMode,都对子View设置了weight属性有了相应的处理
                oldHeight = 0;
                lp.height = LayoutParams.WRAP_CONTENT;
            }

            // 在这个方法内部,最终会调用到子View的measure方法,计算出子View的大小,也就是在这里形成了递归
            measureChildBeforeLayout(
                   child, i, widthMeasureSpec, 0, heightMeasureSpec,
                   totalWeight == 0 ? mTotalLength : 0);

            if (oldHeight != Integer.MIN_VALUE) {
               lp.height = oldHeight;
            }

            // 记录LinearLayout占用的总高度
            final int childHeight = child.getMeasuredHeight();
            final int totalLength = mTotalLength;
            mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
                   lp.bottomMargin + getNextLocationOffset(child));

            // 记录下最高的子View的高度数值
            if (useLargestChild) {
                largestChildHeight = Math.max(childHeight, largestChildHeight);
            }
        }

        // 其他逻辑处理
        ...
    }

    // 添加LinearLayout最底部分割线高度
    if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
        mTotalLength += mDividerHeight;
    }

    // 其他逻辑处理
    ...

    // 添加本身的padding属性值
    mTotalLength += mPaddingTop + mPaddingBottom;

    int heightSize = mTotalLength;

    // 其他逻辑处理
    ...

    // 如果子View设置了weight属性或者在之前的绘制过程中可能跳过某些子View的测量,现在要进行第二次测量计算
    int delta = heightSize - mTotalLength;
    if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
        float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;

        mTotalLength = 0;

        for (int i = 0; i < count; ++i) {
            final View child = getVirtualChildAt(i);

            // 如果子View不可见二次measure依旧忽略
            if (child.getVisibility() == View.GONE) {
                continue;
            }

            // 获取子View的布局属性
            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

            float childExtra = lp.weight;
            // 如果子View设置了weight属性,就进行measure测量
            if (childExtra > 0) {
                // 计算子View通过weight属性获取的拉伸比例
                int share = (int) (childExtra * delta / weightSum);
                weightSum -= childExtra;
                delta -= share;

                    // 获取子View的measureSpec属性
                final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                        mPaddingLeft + mPaddingRight +
                                lp.leftMargin + lp.rightMargin, lp.width);

                // 如果子View已经测量过,在这里进行二次测量
                if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
                    int childHeight = child.getMeasuredHeight() + share;
                    if (childHeight < 0) {
                        childHeight = 0;
                    }

                    child.measure(childWidthMeasureSpec,
                            MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
                } else {//如果子View在第一次测量时候跳过了测量过程,就使用通过设置weight获取的拉伸比例进行第一次测量     
                    child.measure(childWidthMeasureSpec,
                            MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
                                    MeasureSpec.EXACTLY));
                }

                ...
            }

            ...

            final int totalLength = mTotalLength;
            // 记录计算的总高度
            mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
                    lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
        }

        // 添加本身的padding属性
        mTotalLength += mPaddingTop + mPaddingBottom;
        // TODO: Should we recompute the heightSpec based on the new total length?
    } else {// 如果没有跳过任何子View测量过程情况的处理
        ...
    }

  ...
}

       源代码注释已经非常详尽了,从上面源代码中可以了解到,LinearLayout的measure计算过程远远比单个View的measure计算过程要复杂得多,主要是因为作为ViewGroup的容器,会包含子View,需要通过递归调用子View的measure过程,最后才能完整地得出LinearLayout最终的大小。同时也从代码里面得知,如果LinearLayout的子View设置了weight属性的话,会进行额外的测量计算过程,所以在平时使用中尽量的使用RelativeLayout来代替LinearLayout。

       至此关于视图的measure过程都有了大体的认识,如果是单个View,会直接测量计算出自身的大小;如果是ViewGroup,会递归的调用子View的measure()方法,最终得出容器的整体大小,当然ViewGroup只是一个抽象的类,在具体的容器中可能测量的过程有些不同,但是大体还是通过递归依次调用子View的measure()方法,最终完成测量计算。

2.2 Layout

       Layout这个过程是计算视图在父布局中的位置信息,这个过程完成后,就知道视图该显示在屏幕的什么地方了。这个方法会传入四个参数,l,t,r,b分别代表视图在父视图中左,上,右,下的位置,看一下源代码。

/* @param l Left position, relative to parent
 * @param t Top position, relative to parent
 * @param r Right position, relative to parent
 * @param b Bottom position, relative to parent
 */
public void layout(int l, int t, int r, int b) {
    ...
    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_FORCE_LAYOUT;
    mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}
       从源码中可以看见,如果新传入的位置相对于原来有改变或者是强制重新计算,都会调用 onLayout() 方法,那么这个方法到底会做些什么事呢?于是去查看了一下 onLayout() 方法,居然是个抽象方法。
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}
   onLayout 是个抽象方法,那在自定义View的时候,重写的 onLayout 方法里面也没有具体计算过程,但是View还是正确的显示在屏幕上了,这是为什么呢?是什么时候将视图位置信息的保存下来的呢?向上看,会发现调用了 setOpticalFrame 和 setFrame 这个两个方法,这两个方法会接受四个参数,而这四个参数正好可以确定视图在父视图中的位置。直接去查看这两个方法吧。
private boolean setOpticalFrame(int left, int top, int right, int bottom) {
    Insets parentInsets = mParent instanceof View ?
            ((View) mParent).getOpticalInsets() : Insets.NONE;
    Insets childInsets = getOpticalInsets();
    return setFrame(
            left   + parentInsets.left - childInsets.left,
            top    + parentInsets.top  - childInsets.top,
            right  + parentInsets.left + childInsets.right,
            bottom + parentInsets.top  + childInsets.bottom);
}

protected boolean setFrame(int left, int top, int right, int bottom) {
    ...
    mLeft = left;
    mTop = top;
    mRight = right;
    mBottom = bottom;
    mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
}

  setOpticalFrame方法也会调用到setFrame方法,而在setFrame方法中,将四个参数保存了下来,这样就记录下了视图的位置信息。

       这样,视图的位置也记录了下来,如果是ViewGroup的话,会在onLayout方法中,会递归调用子视图的layout方法,这个过程完毕后,View树中所有的View在屏幕中显示的位置都能被确定。因为ViewGroup是个抽象类,onLayout()是个抽象的方法,并没有具体的测量计算过程,我们还是以LinearLayout为例查看一下onLayout()过程。直接从源代码看起。

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);
    }
}

       和LinearLayout自身的 measure() 方法有些类似,都是先查看了本身的方向 orientation 属性,不同的方向处理方式不同,我们也挑一种进行查看,查看一下垂直方向的处理过程, layoutVertical() 方法代码如下。
void layoutVertical(int left, int top, int right, int bottom) {
    final int paddingLeft = mPaddingLeft;

    int childTop;
    int childLeft;

    // LinearLayout的宽度
    final int width = right - left;
    // 子View在父布局中的右边界
    int childRight = width - mPaddingRight;

    // 子View剩余的宽度
    int childSpace = width - paddingLeft - mPaddingRight;

    // 子View的数量
    final int count = getVirtualChildCount();

    // 获取子View的顶部边界
    final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
    final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;

    switch (majorGravity) {
       case Gravity.BOTTOM:
           childTop = mPaddingTop + bottom - top - mTotalLength;
           break;

       case Gravity.CENTER_VERTICAL:
           childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
           break;

       case Gravity.TOP:
       default:
           childTop = mPaddingTop;
           break;
    }

    // 遍历子View,进行计算
    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;
            }
            // 获取子View的左边界
            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;
            }

            // 由于子View是垂直方向排列,每一次计算完毕后,更新子View的顶部边界
            childTop += lp.topMargin;

            // 递归调用子View的`layout()`方法,对子View的位置信息进行测量计算
            setChildFrame(child, childLeft, childTop + getLocationOffset(child),
                    childWidth, childHeight);

            // 添加子View的底部margin值和偏移量,更新子View顶部边界
            childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);

            i += getChildrenSkipCount(child, i);
        }
    }
}

        从代码中可以看出来,LinearLayout的onLayout()方法的测量过程思路非常清晰简单,分别计算了子View的上下左右的边界,然后递归调用了子View的layout()方法,将子Viewd的上下左右边界传入,直至测量完成,计算出LinearLayout在父布局的位置,也计算出LinearLayout中子View在容器中的位置。不同的容器layout()方法实现过程差别很大,大体思路也是通过递归调用子View的layout()方法,从View树的根节点开始测量计算,最终计算出整个View树中各个View在屏幕中的位置。

       最后注意,因为ViewGroup中的onLayout()方法是个抽象方法,所以我们在自定义ViewGroup的时候需要重写这个方法的。

2.3 Draw

       在计算完毕视图的大小和位置之后,终于可以开始绘制视图了。

public void draw(Canvas canvas) {
    ...

    /*
     * 绘制过程会执行以下几个步骤:
     *   1. 绘制view背景
     *   2. 为绘制渐变做准备
     *   3. 绘制view内容
     *   4. 绘制子View
     *   5. 绘制渐变框
     *   6. 绘制其他东西,滑动条等等
     */

    // 步骤1, 绘制view背景
    int saveCount;
    if (!dirtyOpaque) {
        drawBackground(canvas);
    }

    // 通常情况下直接跳过2和5步骤
    final int viewFlags = mViewFlags;
    boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
    boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
    if (!verticalEdges && !horizontalEdges) {
        // 步骤3 绘制view内容
        if (!dirtyOpaque) onDraw(canvas);

        // 步骤4 绘制子View
        dispatchDraw(canvas);

        ...

        // 步骤6 绘制滑动条和前景色等等
        onDrawForeground(canvas);

        // we're done...
        return;
    }
    ...    
}

       从源代码上面看,draw的过程已经非常清楚了,有几个重要的方法需要注意,调用onDraw()方法来绘制本身,调用dispatchDraw()来绘制子View,在ViewGroup中,已经重写好了该方法的,我们在自定义ViewGroup的时候并不需要重写该过程,但是可以捕获到该过程做一些其他的事情。

3. 总结

       单个的View绘制过程非常简单,但如果遇见了ViewGroup绘制过程就会复杂一些,而且对于不同的布局容器,绘制过程也会有一些不同,这个可以通过源代码去查看具体的细节。既然了解了View的绘制过程,我们就可以自定义自己的View了。








ANSYS Fluent中的声学模型:实用指南 文章摘要 本文介绍了ANSYS Fluent中三种气动声学模拟方法:Ffowcs-Williams & Hawkings(FWH)模型、宽带噪声源模型和波动方程模型。FWH模型适用于外部气动声学问题,需要瞬态流场数据来预测远场噪声;宽带模型基于稳态RANS模拟,可快速识别噪声源位置,适合早期设计阶段;波动方程模型直接求解声波方程,提供全域声压分布,但仅适用于低马赫数流动。文章详细说明了各模型的使用场景、设置步骤和关键参数配置,为工程应用提供了实用指南。 阅读详情

相关推荐

电子货架标签应用浅析(ESL)

​【 关注与分享,是对原创最大的鼓励 】 年底了,会断续介绍几个主要的BLE的应用及市场情况,这篇文章介绍的是电子货架标签, ESL (Electronic Shelf Label)。 文章将从应用简介,市场规模,产业链,头部玩家及系统框架进行介绍。只对技术感兴趣的可以直接拉到底。 ESL简介 ESL,是一种显示产品,其主要是替代传统的纸质标签,应用于大型商超,...

iotthings的博客 4691

View的绘制流程

第二步:OnLayout():确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。第三步:OnDraw():绘制视图。3、完全自定义控件:这个View上所展现的内容全部都是我们自己绘制出来的。这种自定义控件不需要我们自己绘制,而是使用原生控件组合成的新控件。View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()

GZXGYZ的博客 394

Bentley ORD(openroads designer) 二次开发(BIM)第七节 在ORD SDK中调用MicroStation SDK

从课程刚开始的时候我们就通过学习,了解了ORD的开发是基于MicroStation的,ORD SDK的接口发布的时候也对MicroStation SDK也做了一次发布。这一章将以我们对ORD SDK知识的了解为基础,使用ORD SDK的接口读取道路几何信息、计算桩号,然后再使用MicroStation SDK 读取地形数据和计算投影点的位置,并创建一组方柱形状的Soild。计算方柱底部坐标要经过两大步骤,通过路线的桩号及偏移距离去定位方柱的平面位置,通过方柱的平面位置计算数字地面模型上的投影点的坐标。

chen286990651的博客 880

View的绘制流程源码解析

View的绘制流程源码分析

xuyin1204的博客 629

pytorch超详细安装教程,Anaconda、PyTorch和PyCharm整套安装流程

本文介绍基于Anaconda环境以及与PyCharm结合,安装PyTorch深度学习框架。

Bluebro的博客 19万+

ServiceNow Discovery & CMDB Interview Questions and Answers (2025)

【代码】ServiceNow Discovery & CMDB Interview Questions and Answers (2025)

weixin_47584092的博客 910

View绘制流程

view和activity的关联,view的measure,layout,draw

a_thinkok的博客 337

PSI5协议技术文档与驱动开发指南

• 供电与数据复用:ECU通过双绞线为传感器提供稳压(典型值5V-11V),同时检测线路电流变化实现数据传输。• 同步信号:ECU发送3.5V电压脉冲(上升沿触发传感器响应),用于时序同步。• 安全气囊:PSI5-P模式实现多碰撞传感器同步采样(响应时间<10ms)• CRC-3(3位):ASIL-B以上功能安全要求(如动力总成传感器)• 奇偶校验(1位):适用于低安全等级场景(如PSI5-P10P模式)• 典型应用:PSI5-P(并行总线)、PSI5-U(通用总线)

u012733413的博客 2867

View绘制流程分析

想要弄清楚View是怎么绘制的得先弄明白View是怎么创建出来的。我们先来看下View的创建流程。总结如下所示:Activity执行onResume之后再ActivityThread中执行Activity的makeVisible方法。View的绘制流程包含了测量大小,测量位置,绘制三个流程;Activity的界面绘制是从mDoctor即根View开始的,也就是从mDoctor的测量大小,测量位置,绘制三个流程;

杨充 1289

Box2D-Lite源码阅读笔记(2)

0.初始化函数 static void InitDemo(int index) { world.Clear(); numBodies = 0; numJoints = 0; bomb = NULL; demoIndex = index; demos[index](bodies, joints); } 在这个初始化函数中,一般会默认执行index=0的函数,这个函数就是一个示例,因为示例比较简单,所以也便于研究,示例效果如下。 就是一个方块。 进入第一个示例函数。(忽略我加的注释)

ARTELE的博客 975

逆向分析顶象5代各类验证码类型的图片还原方法(附源码)

6.刷新滑块验证码图片,会进入之前的for循环断点,分析此处的代码会发现,该循环循环了处理了n字符串的值,取出n字符中的值并使用charCodeAt生成Unicode 编码,然后又调用了a函数,如果a函数返回true就会把之前生成的Unicode编码+1,a函数参数分别是c数组和Unicode取余32后的值,然后再把Unicode取余32后的值push进c数组,这里的n值为滑块碎片背景图的name。2.搜索"ranges":,会找到ranges赋值的地方,在此处代码断点,并点击跳过断点,结束此次调试。

老衲爱洗头的博客 1195

带你从源代码详细分析View的绘制过程

转载注明出处:http://blog.youkuaiyun.com/xiaohanluo/article/details/52084105 1. View简介 &nbsp; &nbsp; &nbsp; &nbsp;View类是Android中各种交互式组件的基类,显示在屏幕上的视图由一个或多个View组成,它们排列在一个View树中,我们可以向树中添加新的View或者View树。 2. Vi...

王三的专栏 4547

下采样优化实战:用ADown显著提升YOLOv11的多尺度特征保持能力

ADown 以“可编程梯度信息+多分支特征融合”打破了传统下采样的性能壁垒,为 YOLOv11 带来了多尺度特征的精准传递能力。从原理到代码集成,本教程已为你铺就了一条“小目标检测精度跃升”的实战路径。现在就动手尝试吧——当你看到模型对极小目标也能精准识别,且特征图细节完整时,你会明白这份技术探索的价值。我很乐意在你实践过程中帮你解决任何问题,让我们一起推动目标检测在下采样领域的边界。

FJN110的博客 421

自定义View(8)View的绘制流程

安卓UI的重点之一就是View的绘制流程,经常出现在面试题中。熟悉View的绘制流程,不仅能轻松通过View相关的面试,也可以让我们更加方便的使用自定义View以及官方View。此篇先以常见面试题为切入点,说明自定义View的重要性,然后又以getMeasuredHeight值的获取作为问题点,带着问题从源码角度分析View的绘制流程。

xunyan6234的博客 920

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

android绘图教程,Android View 绘制流程(Draw)全面解析

前言前几篇文章,笔者分别讲述了DecorView,measure,layout流程等,接下来将详细分析三大工作流程的最后一个流程——绘制流程。测量流程决定了View的大小,布局流程决定了View的位置,那么绘制流程将决定View的样子,一个View该显示什么由绘制流程完成。以下源码均取自Android API 21。从performDraw说起前面几篇文章提到,三大工作流程始于ViewRootIm...

weixin_28487725的博客 524

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Android View 绘制流程 过程分析

前言 今天整理了一下Android View的绘制流程,记录一下 一、如何获取View的宽高 我们经常会遇到要获取控件宽高的情况,可以通过以下两种方式来获取。 1. 通过View的post() MFragmentTabView tabView = findViewById(R.id.fragment_tab_view); tabView.post(new Runnable() { @Override public void run() { tabView.getW

不再烦恼 2041

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

深入理解Android View绘制流程 源码详解

Android View的绘制流程

枫叶的博客 1139

Android-面试官:简述一下-View-的绘制流程,这个都答不出来还想进大厂?

作为一名Android开发者肯定明白View的地位,说它占据半壁江山也不为过,作为基石之一,搞明白它的加载流程是每个开发者都应该去做的,目前网络上很多关于View绘制流程的文章,有些质量也很高,但我还是想以自己的思路出一篇文章。相信读完你对View的工作机制以及自定义View会有一个全新的认识。创建一个类继承View,重写其方法//默认宽//默认高一般的自定义View中,如果对宽高没有特殊需求可直接通过方法获取,该方法位于View中源码如下://默认尺寸。

2501_90425636的博客 780

View的draw过程源码分析

/** * * 原创作者: * 谷哥的小弟 http://blog.youkuaiyun.com/lfdfhl * * 文档描述: * View的draw过程源码分析 * * 绘制过程分为六步: * * 1. Draw the background * 2. If necessary, save the canvas' layer

谷哥的小弟 1982

Android学习笔记之View的绘制流程(一)——measure过程

一句话总结:View的绘制流程是从ViewRoot (ViewRootImpl)的 performTraversals方法开始,经过measure,layout,draw 三个过程才能最终将一个View绘制出来。其中measure用来测量View的宽高,layout用来确定View在父容器中的放置位置,draw用来将View绘制到屏幕上。 再说细点,当界面强制重绘调用了View的requestL...

Ein3614的博客 428

Android View绘制流程(源码分析)

这里写目录标题一 前言二 View绘制2.1 View绘制的三个流程2.2 Android视图层次结构简介三 View绘制流程3.1 DecorView的创建(setContentView)3.2 顶层视图DecorView添加到窗口的过程3.3 ViewRootImpl(DecorView与window沟通的桥梁)3.3.1 执行流程3.4 measure过程分析3.4.1 MeasureSpec**和**ViewGroup.LayoutParams**类3.4.2 View测量的基本流程及重要方法分析3

qq_39431405的博客 1115

View绘制源码浅析(二)布局的测量、布局、绘制

前言 在第一篇View绘制源码浅析(一)布局的加载我们知道了setContentView()完成了DecorView的创建,并且将xml中的布局标签转换成了对应的View、属性转换成了对应的LayoutParams然后添加到了id为content的布局上,也就是说完成了布局对象的创建并且和DecorView关联上了。 那么第二篇将介绍View是如何显示出来的,主体流程可分为测量、布局、绘制这三步。...

朱利源的博客 512

从源码角度分析view的draw过程

前面已经分析了Android view的测量和布局,并且重点介绍了linearLayout的测量机制。特别感谢博乐对我前一篇文章的推荐以及赞赏,我也会一如既往的写好每一篇文章。接下来的这篇文章会以前两篇文章为基础继续分析draw。 1、从源码角度分析Android View的绘制机制(一) 2、从源码角度分析linearLayout测量过程以及weight机制 3、从源码角度分析view的l

相信技术的力量 2410

企业AI开发转型:从功能模块到智能体生态

AI智能体技术正在重塑企业软件开发范式,其核心在于通过自主决策和任务分解能力实现业务目标。不同于传统预设功能模块的开发方式,基于AutoGPT等框架的智能体系统采用ReAct模式进行动态任务规划,结合向量数据库和Transformer模型实现分层记忆。这种架构特别适用于需要复杂决策链的场景,如供应链优化和客户关系管理。在实际落地中,企业需解决任务边界定义、传统系统集成等挑战,同时建立包含目标偏离度监控的新型运维体系。随着LangChain等工具链的成熟,智能体技术已从实验阶段进入工程化应用,为零售、金融等行

weixin_34343308的博客 235

View绘制流程源码解析

View绘制流程源码解析 标签: android源码绘制流程 2017-05-23 17:37 219人阅读 评论(0) 收藏 举报  分类: 自定义View(25)  版权声明:本文为博主原创文章,未经博主允许不得转载。 1.写在前面   很多人在获取控件宽高的时候,遇到了一些问题,因为涉及到的知识点比较广,所以有时我只能说个大概,这

王人冉的博客 388

Android 中View的绘制机制源码分析 四

到目前为止,我们已经学习了View的测量,布局过程,今天我们就来学习一下最后一个过程:绘画 绘画过程和前面的两个过程一样,都是在ViewRoot的performTraversals这个方法中调用的,感兴趣的同学可以找找看,我这里就不在贴出代码了,我们直接看View的draw方法吧View的draw过程的五部曲 /** * Manually render this view (an

Android学习之旅 2653
上一篇: JAVA面试基础1
下一篇: sql输入单引号总显示错误 为什么
AuthFailure
博客等级 码龄10年 0粉丝 2原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值