Android知识程序员Android技术知识

getWidth与getMeasuredWidth的区别,如何在

2017-12-17  本文已影响0人  小鹿啊小鹿

Android学习计划第五周

问题一:getWidth()与getMeasuredWidth()的区别

从源码看区别,首先是getWidth();
image.png

解读:返回你的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的时候被调用:
image.png

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

找一下发现在这个方法中


image.png

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


image.png

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


image.png

那结合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);
            }
        });
    }
}

控制台日志:


image.png
果然好用。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);
            }
        });

控制台输出:


image.png

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

上一篇 下一篇

猜你喜欢

热点阅读