canvas教程

公共技术点( View 绘制流程)(2)

字号+ 作者:H5之家 来源:H5之家 2017-09-07 15:03 我要评论( )

源码分析 /*** 请求所有子 View 去 measure 自己,要考虑的部分有对子 View 的测绘要求 MeasureSpec 以及其自身的 padding* 这里跳过所有为 GONE 状态的子 View,最繁重的工作是在 getChildMeasureSpec 方法中处理

源码分析

/** * 请求所有子 View 去 measure 自己,要考虑的部分有对子 View 的测绘要求 MeasureSpec 以及其自身的 padding * 这里跳过所有为 GONE 状态的子 View,最繁重的工作是在 getChildMeasureSpec 方法中处理的 * * @param widthMeasureSpec 对该 View 的 width 测绘要求 * @param heightMeasureSpec 对该 View 的 height 测绘要求 */ 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); } } } protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { final LayoutParams lp = child.getLayoutParams();//获取 Child 的 LayoutParams final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,// 获取 ChildView 的 widthMeasureSpec mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,// 获取 ChildView 的 heightMeasureSpec mPaddingTop + mPaddingBottom, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } /** * 该方法是 measureChildren 中最繁重的部分,为每一个 ChildView 计算出自己的 MeasureSpec。 * 目标是将 ChildView 的 MeasureSpec 和 LayoutParams 结合起来去得到一个最合适的结果。 * * @param spec 对该 View 的测绘要求 * @param padding 当前 View 在当前唯独上的 paddingand,也有可能含有 margins * * @param childDimension 在当前维度上(height 或 width)的具体指 * @return 子视图的 MeasureSpec */ public static int getChildMeasureSpec(int spec, int padding, int childDimension) { ......... // 根据获取到的子视图的测量要求和大小创建子视图的 MeasureSpec return MeasureSpec.makeMeasureSpec(resultSize, resultMode); } /** * * 用于获取 View 最终的大小,父视图提供了宽、高的约束信息 * 一个 View 的真正的测量工作是在 onMeasure(int, int) 中,由该方法调用。 * 因此,只有 onMeasure(int, int) 可以而且必须被子类复写 * * @param widthMeasureSpec 在水平方向上,父视图指定的的 Measure 要求 * @param heightMeasureSpec 在竖直方向上,控件上父视图指定的 Measure 要求 * */ public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ... onMeasure(widthMeasureSpec, heightMeasureSpec); ... } protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } 4. layout 相关概念及核心方法

首先要明确的是,子视图的具体位置都是相对于父视图而言的。View 的 onLayout 方法为空实现,而 ViewGroup 的 onLayout 为 abstract 的,因此,如果自定义的 View 要继承 ViewGroup 时,必须实现 onLayout 函数。

在 layout 过程中,子视图会调用getMeasuredWidth()和getMeasuredHeight()方法获取到 measure 过程得到的 mMeasuredWidth 和 mMeasuredHeight,作为自己的 width 和 height。然后调用每一个子视图的layout(l, t, r, b)函数,来确定每个子视图在父视图中的位置。

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,为其设置相对父视图的坐标 */ void layoutVertical(int left, int top, int right, int bottom) { for (int i = 0; i < count; i++) { final View child = getVirtualChildAt(i); if (child == null) { childTop += measureNullChild(i); } else if (child.getVisibility() != GONE) {//不需要立即展示的 View 设置为 GONE 可加快绘制 final int childWidth = child.getMeasuredWidth();//measure 过程确定的 Width final int childHeight = child.getMeasuredHeight();//measure 过程确定的 height ...确定 childLeft、childTop 的值 setChildFrame(child, childLeft, childTop + getLocationOffset(child), childWidth, childHeight); } } } private void setChildFrame(View child, int left, int top, int width, int height) { child.layout(left, top, left + width, top + height); } View.java public void layout(int l, int t, int r, int b) { ... setFrame(l, t, r, b) } /** * 为该子 View 设置相对其父视图上的坐标 */ protected boolean setFrame(int left, int top, int right, int bottom) { ... } 5. 绘制流程相关概念及核心方法

先来看下与 draw 过程相关的函数:

View.draw(Canvas canvas): 由于 ViewGroup 并没有复写此方法,因此,所有的视图最终都是调用 View 的 draw 方法进行绘制的。在自定义的视图中,也不应该复写该方法,而是复写onDraw(Canvas)方法进行绘制,如果自定义的视图确实要复写该方法,那么请先调用super.draw(canvas)完成系统的绘制,然后再进行自定义的绘制。

View.onDraw():
View 的onDraw(Canvas)默认是空实现,自定义绘制过程需要复写的方法,绘制自身的内容。

dispatchDraw() 发起对子视图的绘制。View 中默认是空实现,ViewGroup 复写了dispatchDraw()来对其子视图进行绘制。该方法我们不用去管,自定义的 ViewGroup 不应该对dispatchDraw()进行复写。

绘制流程图

MeasureLayout img

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • 前奏:自定义View(一)onDraw()中一些常用的属性和方法总结

    前奏:自定义View(一)onDraw()中一些常用的属性和方法总结

    2017-08-31 11:08

  • SurfaceView 典型用法

    SurfaceView 典型用法

    2017-08-30 10:03

  • 在IE下动态创建canvas使用excanvas时失效解决方法

    在IE下动态创建canvas使用excanvas时失效解决方法

    2017-08-22 15:04

  • JavaScript在IE中“意外地调用了方法或属性访问”

    JavaScript在IE中“意外地调用了方法或属性访问”

    2017-08-14 15:00

网友点评