canvas教程

自定义view之view显示流程(4)

字号+ 作者:H5之家 来源:H5之家 2017-04-18 17:04 我要评论( )

在onMeasure的第一轮测量中有个比较关键的方法measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0),从名字可以看出这个方法是测量子view的,并且还会将子view的margin值考虑在内。我们可

在onMeasure的第一轮测量中有个比较关键的方法measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0),从名字可以看出这个方法是测量子view的,并且还会将子view的margin值考虑在内。我们可以接着看下这个方法的源码。

/** * Ask one of the children of this view to measure itself, taking into * account both the MeasureSpec requirements for this view and its padding * and margins. The child must have MarginLayoutParams The heavy lifting is * done in getChildMeasureSpec. * * @param child The child to measure * @param parentWidthMeasureSpec The width requirements for this view * @param widthUsed Extra space that has been used up by the parent * horizontally (possibly by other children of the parent) * @param parentHeightMeasureSpec The height requirements for this view * @param heightUsed Extra space that has been used up by the parent * vertically (possibly by other children of the parent) */ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }

可以看出这个方法仍然是先生成了子view的MeasureSpec,然后调用子view的measure方法。需要注意的是传入的各个参数。查看getChildMeasureSpec(int spec, int padding, int childDimension)方法,我们知道它的三个参数分别为:
- int spec:该view的MeasureSpec,这里就是FrameLayout的Spec。
- int padding:这并不是单纯字面意思的父layout的padding值,简而言之,这就是父layout已经被占用掉的空间。如果要生成的是HeightMeasureSpec,那么这个padding就包括其他子view在垂直方向已经占用掉的位置、父layout的paddingTop和paddingBottom、子view本身的topMargin和bottomMargin值。
- int childDimension:要获取MeasureSpec的子view的宽度或者高度值,这个值是确定大小、wrap_content和match_parent这三种情况之一。

在onMeasure方法中第一轮测量子view的写法是

measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);

显然传给这个方法的widthUsed和heightUsed值是0,到这个方法内部的getChildMeasureSpec(int spec, int padding, int childDimension)中的padding就不包括其他子view已经占用掉的位置。这也恰好符合FrameLayout的特点:所有的子view都重叠排列,互相之间不影响。并且Framlayout如果是wrap_content的,那么它的宽是所有子view中最大的宽、高是所有子view中最大的高。

1.3 measure过程总结

以上就是FrameLayout完整的测量过程,当然FrameLayout本身布局特点就是非常简单的,如果是RelativeLayout,那么测量过程会更加复杂。但即使如此,我们还是能从中总结出Layout测量的一般规律的:

这样就完成了layout及其子view的测量过程。而view的测量就更简单了,因为没有子view,只要确定了自身内容的大小,再结合MeasureSpec便可以测量完毕。比如有个view需要画个圆,那么只要考虑设置的padding值以及圆的大小即可。

注意:三种宽、高值的区别:
1. LayoutParams.width、LayoutParams.height:这个是布局文件中的宽度和高度值,单位是px,并且WRAP_CONTENT对应-2,MATHCH_PARENT对应-1,这个是任何时候都可以调用的。特别注意,无论是measure还是layout过程,都不会对这个LayoutParams产生影响,除非在代码中手动调用setLayoutParams()方法来设置,否则LayoutParams中存储的都是布局文件中的宽和高,既不是测量出来的宽和高,也不是最终确定的宽和高。可以做个实验,初始化任意一个控件时,使用ViewTreeObserver添加ViewTreeObserver.OnGlobalLayoutListener,并在回调函数中打印log来分别显示使用LayoutParams获取控件宽和高,以及直接使用getWidth()和getHeight()来获取宽和高,就可以看到效果。从这方面来说,其实LayoutParams更像是一种基准,是给父layout为该view生成MeasureSpec时参考用的,而不一定确切就是其中的值。所以如果要获得一个控件的真实宽和高,一定不要使用LayoutParams。可以通过setLayoutPramas(LayoutParams param)方法来改变view的宽和高,而且往往这是唯一可以手动指定宽和高的方法,比如虽然TextView有setWidth(int width)方法,但是ImageView却没有,只能通过setLayoutPramas(LayoutParams param)。
2. getMeasuredWidth()、getMeasuredHeight():获取测量出的宽和高,这是在measure方法结束后才可以得到有效值。
3. getWidth()、getHeight():获取最终实际的宽和高,实际的宽和高在layout阶段才会确定,但是大部分情况,测量出的宽和高就是最终的宽和高。

2. layout过程

layout相比measure,就比较简单,而且不像measure是有固定套路,基本实现方式比较自由。和measure同理,在自定义view的时候应该重写onLayout(),虽然layout()方法是可以重写的。

2.1 理解layout过程

同样为了简单,我们继续选取FrameLayout的layout过程来学习。也许大家会说,FrameLayout的布局太简单了,不就是所有view都靠着左上角布局吗?其实并不是这么简单的,FrameLayout也有可以控制子view位置的参数,并且在布局过程中我们会看到某些需要在自定义view中需要注意的事情。接下来就看一下layout函数。需要注意的是layout函数是在View类中定义的,并且FrameLayout遵循了规范,并没有重写layout函数,因此需要到View类中找到layout函数。

/** * Assign a size and position to a view and all of its * descendants * * <p>This is the second phase of the layout mechanism. * (The first is measuring). In this phase, each parent calls * layout on all of its children to position them. * This is typically done using the child measurements * that were stored in the measure pass().</p> * * <p>Derived classes should not override this method. * Derived classes with children should override * onLayout. In that method, they should * call layout on each of their children.</p> * * @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 */ @SuppressWarnings({"unchecked"}) 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; }

 

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

相关文章
  • Android开发基本常识及技巧

    Android开发基本常识及技巧

    2017-04-13 16:02

  • Android Canvas练习题(6)饼图(Pie Chart)百分比标注位置计算技巧

    Android Canvas练习题(6)饼图(Pie Chart)百分比标注位置计算技巧

    2017-04-12 11:00

  • Android Canvas clipPath 画图锯齿问题

    Android Canvas clipPath 画图锯齿问题

    2017-04-11 11:04

  • 实现Unity和Android进行交互

    实现Unity和Android进行交互

    2017-04-10 09:02

网友点评
A