问题:
1.view的getWidth()和getMeasuredWidth()有什么区别
2.如何在oncreate()中拿到view的宽度和高度
回答:
(1)
getWidth 获取的是这个view最终显示的大小,这个大小有可能等于原始的大小也有可能不等于原始大小
return mRight - mLeft;(在onlayout()过程中获取)
getMeasuredWidth 此视图的原始测量宽度,也就是这个view在XML文件中配置或者是代码中设置的大小。
return mMeasuredWidth & MEASURED_SIZE_MASK;(在onMeasure()中获取)
区别:一般情况下getWidth 和getMeasuredWidth 值是一样的但自身或父view在onlayout调用完毕后
再设置onMeasure的参数,getMeasuredWidth将被改变,getWidth 不变.
详细过程:见下方
(2)
3种方法 :
1.View.MeasureSpec.makeMeasureSpec(200,View.MeasureSpec.EXACTLY); view_test.measure(w, h);
2.view_test.post
3.view_test.getViewTreeObserver().addOnGlobalLayoutListener
详细过程:见下方
执行过程:
I/viewActivity: view1 getWidth:0 getMeasuredWidth:0
I/viewActivity: onCreate
I/viewActivity: onStart
I/viewActivity: onResume
I/viewActivity: onAttachedToWindow
I/viewActivity: onMeasure getWidth:0 getMeasuredWidth:1079
I/viewActivity: onMeasure getWidth:0 getMeasuredWidth:1080
I/viewActivity: onSizeChanged getWidth:1080 getMeasuredWidth:1080
I/viewActivity: onLayout getWidth:1080 getMeasuredWidth:1080
view_test.getViewTreeObserver
view_test.post
I/viewActivity: onDraw getWidth:1080 getMeasuredWidth:1080
(1.1)getMeasuredWidth 的获取 return mMeasuredWidth & MEASURED_SIZE_MASK;
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
/* getSuggestedMinimumWidth()返回视图应该使用的最小宽度。
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
*/
}
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom;
measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
-----------------getDefaultSize解释:
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;
}
public static int getMode(int measureSpec) {
//noinspection ResourceType
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
(1.2)getWidth()的获取return mRight - mLeft;
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;
//isLayoutModeOptical(mParent)判断是是否是viewgroup
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
.............
}
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) {
.............
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
}
.............
return changed;
}
(2.1) view.measure
int w = View.MeasureSpec.makeMeasureSpec(200,View.MeasureSpec.EXACTLY);
int h = View.MeasureSpec.makeMeasureSpec(200,View.MeasureSpec.EXACTLY);
view_test.measure(w, h);
// view_test.layout(0, 0, w, h);
int measuredwidth =view_test.getMeasuredWidth();//测量值已获得
int width =view_test.getWidth(); //getWidth在调用layout后获得
MeasureSpec:由32位2进制组成,前两位为mode后面为size,size最大值为(1<<30)-1即2^30-1
(2.2) view.post
利用handler通信机制,发送一个runble到message queue中layout完成时,自动发送消息通知UI线程
view_test.post(new Runnable() {
@Override
public void run() {
Log.i(TAG,"TestPost getWidth:"+view_test.getWidth());
}
});
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) { //在onCreate的方法中执行的时候,attachInfo==null
return attachInfo.mHandler.post(action);
}
在onCreate中调用View.post的时候,post的Runnable被保存起来,在下一次遍历布局的时候重新加到消息队列里(Android 7.0是在attach window的时候重新加到消息队列里),而且布局遍历本身也是加到消息队列执行的,所以我们post的Runnable所在的消息肯定是在布局遍历之后,所以在Runnable里必定可以获取到正确的宽高。
(2.3) view.getViewTreeObserver
监听view的onlayout绘制过程,一旦layout触发变化,立即回调onlayoutchange方法
view_test.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Log.i(TAG,"TestViewTreeObserver getWidth:"+view_test.getWidth());
//移除监听避免每一次 view变化都触发该事件
view_test.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});