View.java
/**
*<p>
*This is called to find out how big a view should be. The parent
*supplies constraint information in the width and height parameters.
*</p>
*
*<p>
*The actual mesurement work of a view is performed in
*{@link #onMeasure(int, int)},called by this method. Therefore, only
*{@link #onMeasure(int, int)}can and must be overriden by subclasses.
*</p>
*
*
*@paramwidthMeasureSpec Horizontal space requirements as imposed by the
* parent
*@paramheightMeasureSpec Vertical space requirements as imposed by the
* parent
*
*@see#onMeasure(int, int)
*/
publicfinalvoidmeasure(intwidthMeasureSpec, intheightMeasureSpec) {
if((mPrivateFlags& FORCE_LAYOUT)== FORCE_LAYOUT||
widthMeasureSpec !=mOldWidthMeasureSpec||
heightMeasureSpec !=mOldHeightMeasureSpec){
//first clears the measured dimension flag
mPrivateFlags&= ~MEASURED_DIMENSION_SET;
if(ViewDebug.TRACE_HIERARCHY){
ViewDebug.trace(this,ViewDebug.HierarchyTraceType.ON_MEASURE);
}
//measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec,heightMeasureSpec); //重点是它
//flag not set, setMeasuredDimension() was not invoked, we raise
//an exception to warn the developer
if((mPrivateFlags& MEASURED_DIMENSION_SET)!= MEASURED_DIMENSION_SET){
thrownewIllegalStateException("onMeasure()did not set the"
+ "measured dimension by calling"
+ "setMeasuredDimension()");
}
mPrivateFlags|= LAYOUT_REQUIRED;
}
mOldWidthMeasureSpec= widthMeasureSpec;
mOldHeightMeasureSpec= heightMeasureSpec;
}
/**
*<p>
*Measure the view and its content to determine the measured width andthe
*measured height. This method is invoked by {@link#measure(int, int)} and
*should be overriden by subclasses to provide accurate and efficient
*measurement of their contents.
*</p>
*
*<p>
*<strong>CONTRACT:</strong>When overriding this method, you
*<em>must</em>call {@link #setMeasuredDimension(int,int)} to store the
*measured width and height of this view. Failure to do so will triggeran
*<code>IllegalStateException</code>,thrown by
*{@link #measure(int, int)}.Calling the superclass'
*{@link #onMeasure(int, int)}is a valid use.
*</p>
*
*<p>
*The base class implementation of measure defaults to the backgroundsize,
*unless a larger size is allowed by the MeasureSpec. Subclasses should
*override {@link #onMeasure(int, int)}to provide better measurements of
*their content.
*</p>
*
*<p>
*If this method is overridden, it is the subclass's responsibility tomake
*sure the measured height and width are at least the view's minimumheight
*and width ({@link#getSuggestedMinimumHeight()} and
*{@link #getSuggestedMinimumWidth()}).
*</p>
*
*@paramwidthMeasureSpec horizontal space requirements as imposed by theparent.
* The requirements are encoded with
* {@linkandroid.view.View.MeasureSpec}.
*@paramheightMeasureSpec vertical space requirements as imposed by theparent.
* The requirements are encoded with
* {@linkandroid.view.View.MeasureSpec}.
*
*@see#getMeasuredWidth()
*@see#getMeasuredHeight()
*@see#setMeasuredDimension(int, int)
*@see#getSuggestedMinimumHeight()
*@see#getSuggestedMinimumWidth()
*@seeandroid.view.View.MeasureSpec#getMode(int)
*@seeandroid.view.View.MeasureSpec#getSize(int)
*/
protectedvoidonMeasure(intwidthMeasureSpec, intheightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec));
}
每个view中一定存在一个必调的方法,measure(int, int) ,它调用onMeasure(int,int)
onMeasure(widthMeasureSpec,heightMeasureSpec)的作用是什么?
测量view和它的content得出widthMeasureSpec heightMeasureSpec 并保存下来。
measure(widthMeasureSpec,heightMeasureSpec)的作用是什么?
通过父布局提供的 widthMeasureSpec, widthMeasureSpec来确定这个View的 大小,有多高,有多宽,
特别对于一些实现ViewGroup 的控件 在某些情况下,当 android:layout_heightandroid:layout_width 不能达到目的时,可以通过重写 onMeasure(int,int) 来达到调整view高宽的目的。
/**
*<p>Thismehod must be called by {@link#onMeasure(int, int)} to store the
*measured width and measured height. Failing to do so will trigger an
*exception at measurement time.</p>
*
*@parammeasuredWidth The measured width of this view. May be a complex
*bit mask as defined by {@link#MEASURED_SIZE_MASK} and
*{@link #MEASURED_STATE_TOO_SMALL}.
*@parammeasuredHeight The measured height of this view. May be a complex
*bit mask as defined by {@link#MEASURED_SIZE_MASK} and
*{@link #MEASURED_STATE_TOO_SMALL}.
*/
protectedfinalvoidsetMeasuredDimension(intmeasuredWidth, intmeasuredHeight) {
mMeasuredWidth= measuredWidth;
mMeasuredHeight= measuredHeight;
mPrivateFlags|= MEASURED_DIMENSION_SET;
}
setMeasuredDimension在onMeasure(int,int) 中调用,用来存储跟新测量的宽和高,若不调,可能产生异常。
转载:
MeasureSpec介绍及使用详解
MeasureSpec是view中的内部类
一个MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求。一个MeasureSpec由大小和模式组成。它有三种模式:UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;AT_MOST(至多),子元素至多达到指定大小的值。
1父布局传下来的
2子元素布局文件或代码中设置的
它常用的三个函数:
1.staticintgetMode(int measureSpec):根据提供的测量值(格式)提取模式(上述三个模式之一)
2.staticintgetSize(int measureSpec):根据提供的测量值(格式)提取大小值(这个大小也就是我们通常所说的大小)
3.staticintmakeMeasureSpec(int size,int mode):根据提供的大小值和模式创建一个测量值(格式)
这个类的使用呢,通常在view组件的onMeasure方法里面调用但也有少数例外,看看几个例子:
a.首先一个我们常用到的一个有用的函数,View.resolveSize(intsize,intmeasureSpec)
publicstatic intresolveSize(intsize, int measureSpec) {
returnresolveSizeAndState(size,measureSpec, 0) & MEASURED_SIZE_MASK;
}
publicstatic intresolveSizeAndState(intsize, int measureSpec, intchildMe asuredState) {
intresult = size;
intspecMode = MeasureSpec.getMode(measureSpec);
intspecSize = MeasureSpec.getSize(measureSpec);
switch(specMode) {
caseMeasureSpec.UNSPECIFIED:
result = size;
break;
caseMeasureSpec.AT_MOST:
if(specSize < size) {
result = specSize |MEASURED_STATE_TOO_SMALL;
} else{
result = size;
}
break;
caseMeasureSpec.EXACTLY:
result = specSize;
break;
}
returnresult | (childMeasuredState&MEASURED_STATE_MASK);
}
简单说一下,这个方法的主要作用就是根据你提供的大小和MeasureSpec,返回你想要的大小值,这个里面根据传入模式的不同来做相应的处理。
再看看MeasureSpec.makeMeasureSpec方法,实际上这个方法很简单:
publicstatic intmakeMeasureSpec(int size, intmode) {
returnsize + mode;
}
这样大家不难理解size跟measureSpec区别了。
******************************************
布局步骤
1measure onMeasure(measureWidth, measureHight) 计算大小
2layout onLayout(chang, l, t, r, b) 给自己及 子view分配size,position
3draw ondraw(canvas) 画图
dispatchDraw 空方法体
getWidth()getMeasureWidth() 区别
getWidth():view的布局完成后,view的宽度
getMeasureWidth():这是一个过程量,得到的是在最近一次調用measure()方法測量後得到的view的寬度,它僅僅用在測量和layout的計算中。
举例:有时会通过 child.getMeasureWidth()的值根据 Gravity重心得出 view的4个lt r b 再layout
如Gridview 中存在一个方法:
privatevoidsetupChild(View child, intposition, inty, booleanflow, intchildrenLeft,
booleanselected, booleanrecycled, intwhere) {
booleanisSelected = selected && shouldShowSelector();
finalbooleanupdateChildSelected = isSelected != child.isSelected();
finalintmode = mTouchMode;
finalbooleanisPressed = mode > TOUCH_MODE_DOWN && mode <TOUCH_MODE_SCROLL &&
mMotionPosition ==position;
finalbooleanupdateChildPressed = isPressed != child.isPressed();
booleanneedToMeasure = !recycled || updateChildSelected ||child.isLayoutRequested();
//Respect layout params that are already in the view. Otherwise make
//some up...
AbsListView.LayoutParams p =(AbsListView.LayoutParams)child.getLayoutParams();
if(p == null){
p = newAbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,0);
}
p.viewType =mAdapter.getItemViewType(position);
if(recycled && !p.forceAdd) {
attachViewToParent(child,where, p);
} else{
p.forceAdd = false;
addViewInLayout(child,where, p, true);
}
if(updateChildSelected) {
child.setSelected(isSelected);
if(isSelected) {
requestFocus();
}
}
if(updateChildPressed) {
child.setPressed(isPressed);
}
if(mChoiceMode != CHOICE_MODE_NONE&& mCheckStates != null){
if(child instanceofCheckable) {
((Checkable)child).setChecked(mCheckStates.get(position));
} elseif(getContext().getApplicationInfo().targetSdkVersion
>=android.os.Build.VERSION_CODES.HONEYCOMB){
child.setActivated(mCheckStates.get(position));
}
}
if(needToMeasure) {
intchildHeightSpec = ViewGroup.getChildMeasureSpec(
MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED),0, p.height);
intchildWidthSpec = ViewGroup.getChildMeasureSpec(
MeasureSpec.makeMeasureSpec(mColumnWidth,MeasureSpec.EXACTLY),0, p.width);
child.measure(childWidthSpec,childHeightSpec);
} else{
cleanupLayoutState(child);
}
finalintw = child.getMeasuredWidth();
final int h =child.getMeasuredHeight();
intchildLeft;
finalintchildTop = flow ? y : y - h;
finalintlayoutDirection = getResolvedLayoutDirection();
finalintabsoluteGravity = Gravity.getAbsoluteGravity(mGravity,layoutDirection);
switch(absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK){
caseGravity.LEFT:
childLeft =childrenLeft;
break;
caseGravity.CENTER_HORIZONTAL:
childLeft = childrenLeft+ ((mColumnWidth - w) / 2);
break;
case Gravity.RIGHT:
childLeft = childrenLeft+ mColumnWidth - w;
break;
default:
childLeft =childrenLeft;
break;
}
if(needToMeasure) {
finalintchildRight = childLeft + w;
finalintchildBottom = childTop + h;
child.layout(childLeft,childTop, childRight, childBottom);
} else{
child.offsetLeftAndRight(childLeft- child.getLeft());
child.offsetTopAndBottom(childTop- child.getTop());
}
if(mCachingStarted) {
child.setDrawingCacheEnabled(true);
}
if(recycled &&(((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)
!= position) {
child.jumpDrawablesToCurrentState();
}
}
ViewGroup.java
1 measureChildren(measureWidth,measureHight) measureChild(child, widthMeasureSpec, heightMeasureSpec)
2继承layout(l,t, r, b) onLayout(chang,l, t, r, b)
3继承 dispatchDraw drawChild