Android学习计划第五周
问题一:getWidth()与getMeasuredWidth()的区别
从源码看区别,首先是getWidth();

解读:返回你的view的宽度,单位是像素。但是这里面的像素值是通过mRight 与mLeft的差值得到的,那么就需要去看看这两个变量代表的是什么。
查看源码发现mRight两个赋值的地方,第一个地方是方法是setRight()方法
/**
* Sets the right position of this view relative to its parent. This method is meant to be called
* by the layout system and should not generally be called otherwise, because the property
* may be changed at any time by the layout.
*
* @param right The right of this view, in pixels.
*/
public final void setRight(int right) {
if (right != mRight) {
final boolean matrixIsIdentity = hasIdentityMatrix();
if (matrixIsIdentity) {
if (mAttachInfo != null) {
int maxRight;
if (right < mRight) {
maxRight = mRight;
} else {
maxRight = right;
}
invalidate(0, 0, maxRight - mLeft, mBottom - mTop);
}
} else {
// Double-invalidation is necessary to capture view's old and new areas
invalidate(true);
}
int oldWidth = mRight - mLeft;
int height = mBottom - mTop;
mRight = right;
mRenderNode.setRight(mRight);
sizeChange(mRight - mLeft, height, oldWidth, height);
......
}
}
这个方法注释已经解释了该方法一般情况下是被系统调用,其他人不能调用,因为layout布局可能会随时改变。
所以可以看出这个方法是用来处理布局改变时的一些操作的,说明不是原始的布局。接着看......
第二个给mRight赋值的地方是setFrame()方法,以下为详细代码:
/**
* Assign a size and position to this view.
*
* This is called from layout.
*
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
* @return true if the new size and position are different than the
* previous ones
* {@hide}
*/
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
// Remember our drawn bit
int drawn = mPrivateFlags & PFLAG_DRAWN;
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;
......
return changed;
}
那么这个setFrame方法什么时候调用呢?注释也说了只能被layout调用,那这个layout到底是什么?
这有点涉及到view的绘制过程。
android中一个view的绘制一般包含三个过程:
第一个过程是去测量(measure),当这个过程执行完毕之后,getMeasuredWidth这个时候也就有了对应的数值。
第二个过程是布局(layout)过程。该过程在代码中就是设置其成员变量mLeft,mTop,mRight,mBottom的值,这个时候就应该明白那些一般情况只能被layout调用的方法的作用了。也就是说的那个layout过程执行完毕的时候,你的getWidth也就有了对应的值。
当然view的绘制,最后一个过程当然是去绘制,即draw.
那么就很清楚了,getWidth所得到的数值是当view绘制流程走完layout这个过程的时候。
所以想要知道getWidth和getMeasuredWidth的区别,最好先熟悉view的绘制流程。
接下来就简单多了,验证一下看看getMeasuredWidth这个方法是不是在measure的时候被调用:

注释告诉我们这个方法等同于这个方法getMeasuredWidthAndState();并且返回值是 测量到的这个view的宽度。那么首先看看这个mMeasuredWidth 在哪赋值。
找一下发现在这个方法中

找了一下发现setMeasuredDimensionRaw方法调用的地方有两个,一个是measure()方法,另一个是(只看view中,因为是view的绘制)在onMeasure();

此处调用了setMeasuredDimension,以下为该方法,调用了setMeasuredDimension。

那结合view的绘制过程应该很清楚了,在masure过程完成时 ,对应的getMeasuredWidth()也就有了对应的值。
总结一下:getMeasuredWidth与getWidth的区别
①:赋值时机不同
getMeasuredWidth是当view绘制流程中的measure流程结束之后有值
getWidth是当view的绘制流程中的layout流程结束之后有值
②:数值含义不同
getMeasuredWidth获取的是view的测量宽度
getWidth获取的是view的实际宽度
正常情况下这个两个数值都是相同的,除非在measure与layout过程之后调用了measure(int widthMeasureSpec, int heightMeasureSpec)方法去改变对应的数值。
问题二:如何在onCreate()方法中获取view的高度和宽度?
难点:onCreate的时候刚开始去绘制视图,我们知道视图绘制会经过measure、layout、和onDraw过程,但是却不知道具体的时间节点,所以直接在onCreate里面去获取view的宽高,得到的都是0。
(这个时候不会只能去查资料。。。)
方法一:资料说view已经想到了这种情况,所以给你提供了一个方法,那就是View.post方法
以下是代码:
public class MainActivity extends AppCompatActivity {
View view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view = findViewById(R.id.view);
int getWidth = view.getWidth();
int getMeasuredWidth = view.getMeasuredWidth();
Log.e("hxl","onCreate:getWidth:"+getWidth);
Log.e("hxl","onCreate:getMeasuredWidth:"+getMeasuredWidth);
view.post(new Runnable() {
@Override
public void run() {
int width = view.getWidth();
int measuredWidth = view.getMeasuredWidth();
Log.e("hxl","post:getWidth:"+width);
Log.e("hxl","post:getMeasuredWidth:"+measuredWidth);
}
});
}
}
控制台日志:

果然好用。view.post机制以后再仔细研究。猜测应该是在view绘制完成之后加了一个回调监听。
方法二:view.addOnLayoutChangeListener这个方法看变量名就知道当layout发生变化的时候会调用,那我们试一下:
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
int width = view.getWidth();
int measuredWidth = view.getMeasuredWidth();
Log.e("hxl","onLayoutChange:getWidth:"+width);
Log.e("hxl","onLayoutChange:getMeasuredWidth:"+measuredWidth);
}
});
控制台输出:

可以看到这个监听执行了两次,可以获取到对应的数值,不过需要注意的是这个时候最好不要去绘制UI
网友评论