getWidth()与getMeasuredWidth()的区别

getWidth()与getMeasuredWidth()的区别

一般在自定义控件的时候getMeasuredWidth/getMeasuredHeight它的赋值在View的setMeasuredDimension中,所以有时可以在onMeasure方法中看到利用getMeasuredWidth/getMeasuredHeight初始化别的参数。而getWidth/getHeight一直在onLayout完成后才会被赋值。一般情况下,如果都完成了赋值,两者值是相同的.(摘自鸿洋)
参考:(Android视图绘制流程完全解析,带你一步步深入了解View(二))

关于View的onMeasure()、onSizeChanged()、onLayout()、onDraw()调用顺序
调用顺序应该是 构造函数——->onMeasure——->onSizeChanged——->onLayout——->onDraw
后面有可能 onMeasure——->onLayout——->onDraw
(摘自here)
并且是在onResume之后。

需要注意的是,在setMeasuredDimension()方法调用之后,我们才能使用getMeasuredWidth()和getMeasuredHeight()来获取视图测量出的宽高,以此之前调用这两个方法得到的值都会是0。

视图大小的控制是由父视图、布局文件、以及视图本身共同完成的,父视图会提供给子视图参考的大小,而开发人员可以在XML文件中指定视图的大小,然后视图本身会对最终的大小进行拍板。
参考:Android视图绘制流程完全解析,带你一步步深入了解View(二)

onMeasure多次调用问题

Android如何绘制视图,解释了为何onMeasure有时要调用多次
为什么FlowLayout的 onMeasure方法会执行4次,onLayout方法会执行两次呢,

父视图可能在它的子视图上调用一次以上的measure(int,int)方法。例如,父视图可以使用unspecified dimensions来将它的每个子视图都测量一次来算出它们到底需要多大尺寸,如果所有这些子视图没被限制的尺寸的和太大或太小,那么它会用精确数值再次调用measure()(也就是说,如果子视图不满意它们获得的区域大小,那么父视图将会干涉并设置第二次测量规则)。

测试:

@SuppressWarnings("deprecation")
public class MyActivity extends FragmentActivity {

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test3);

        Display display = getWindowManager().getDefaultDisplay();
        int displayWidth = display.getWidth();
        int displayHeight = display.getHeight();

        Log.e("111", "display.getWidth()==" + displayWidth);//1080
        Log.e("111", "display.getHeight()==" + displayHeight);//1812
        Log.e("111", "densityDpi==" + getResources().getDisplayMetrics().densityDpi);//480
        Log.e("111", "density==" + getResources().getDisplayMetrics().density);//3.0

        int statusHeight = getStatusHeight(this);
        Log.e("111", "statusHeight==" + statusHeight);//72
        Log.e("111", "displayHeight-statusHeight==" + (displayHeight - statusHeight));//1740

        int actionBarHeight = getActionBarHeight(this);
        Log.e("111", "actionBarHeight==" + actionBarHeight);//168
        Log.e("111", "statusHeight+statusHeight==" + (statusHeight + actionBarHeight));//240

        Log.e("111", "displayHeight-(statusHeight + actionBarHeight)==" + (displayHeight - (statusHeight + actionBarHeight)));//1572

    }

    public int getStatusHeight(Context context) {
        int statusHeight = -1;
        try {
            Class clazz = Class.forName("com.android.internal.R$dimen");
            Object object = clazz.newInstance();
            int height = Integer.parseInt(clazz.getField("status_bar_height")
                    .get(object).toString());
            statusHeight = context.getResources().getDimensionPixelSize(height);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return statusHeight;
    }

    public int getActionBarHeight(Context context) {
        TypedValue localTypedValue = new TypedValue();
        if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, localTypedValue, true)) {
            return TypedValue.complexToDimensionPixelSize(localTypedValue.data, context.getResources().getDisplayMetrics());
        }
        return 0;
    }

}

布局:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#e5e5e5"
    android:orientation="vertical">

    <com.android.imooc.RoundImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#6e6e6e"/>
LinearLayout>
@SuppressLint("AppCompatCustomView")
public class RoundImageView extends ImageView {

    private Paint mPaint;
    private Bitmap bitmap;
    private Matrix mMatrix;
    private int mWidth;
    private int mRadius;
    private BitmapShader bitmapShader;
    private RectF mRoundRect;


    public RoundImageView(Context context) {
        super(context);
        init();
    }

    public RoundImageView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RoundImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mMatrix = new Matrix();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);


        Log.e("111", ".......onMeasure.......");
        Log.e("111", "0getHeight......." + getHeight());//0
        Log.e("111", "0getWidth......." + getWidth());//0
        //1560->1740(调用了两次onmeasure,第二次的1740=屏幕高1812-状态栏高72)
        Log.e("111", "0getMeasuredHeight......." + getMeasuredHeight());
        //1080->1080(调用了两次onmeasure)
        Log.e("111", "0getMeasuredWidth......." + getMeasuredWidth());
        Log.e("111", "0getSuggestedMinimumHeight......." + getSuggestedMinimumHeight());//0
        Log.e("111", "0getSuggestedMinimumWidth......." + getSuggestedMinimumWidth());//0

        mWidth = Math.min(getMeasuredWidth(), getMeasuredHeight());
        mRadius = mWidth / 2;
        setMeasuredDimension(mWidth, mWidth);//1080

        Log.e("111", ".......onMeasure......................");
        Log.e("111", "0getHeight......." + getHeight());//0
        Log.e("111", "0getWidth......." + getWidth());//0
        Log.e("111", "0getMeasuredHeight......." + getMeasuredHeight());//1080->1080
        Log.e("111", "0getMeasuredWidth......." + getMeasuredWidth());//1080-->1080
        Log.e("111", "0getSuggestedMinimumHeight......." + getSuggestedMinimumHeight());//0
        Log.e("111", "0getSuggestedMinimumWidth......." + getSuggestedMinimumWidth());//0

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        Log.e("111", ".......onSizeChanged.......");
        Log.e("111", "1getHeight......." + getHeight());//1080(setMeasuredDimension决定)
        Log.e("111", "1getWidth......." + getWidth());//1080
        Log.e("111", "1getMeasuredHeight......." + getMeasuredHeight());//1080
        Log.e("111", "1getMeasuredWidth......." + getMeasuredWidth());//1080
        Log.e("111", "1getSuggestedMinimumHeight......." + getSuggestedMinimumHeight());//0
        Log.e("111", "1getSuggestedMinimumWidth......." + getSuggestedMinimumWidth());//0

        mRoundRect = new RectF(0, 0, getWidth(), getHeight());
    }


    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        Log.e("111", ".......onLayout.......");
        Log.e("111", "2getHeight......." + getHeight());//1080
        Log.e("111", "2getWidth......." + getWidth());//1080
        Log.e("111", "2getMeasuredHeight......." + getMeasuredHeight());//1080
        Log.e("111", "2getMeasuredWidth......." + getMeasuredWidth());//1080
        Log.e("111", "2getSuggestedMinimumHeight......." + getSuggestedMinimumHeight());//0
        Log.e("111", "2getSuggestedMinimumWidth......." + getSuggestedMinimumWidth());//0
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Log.e("111", ".......onDraw.......");
        Log.e("111", "3getHeight......." + getHeight());//1080
        Log.e("111", "3getWidth......." + getWidth());//1080
        Log.e("111", "3getMeasuredHeight......." + getMeasuredHeight());//1080
        Log.e("111", "3getMeasuredWidth......." + getMeasuredWidth());//1080
        Log.e("111", "3getSuggestedMinimumHeight......." + getSuggestedMinimumHeight());//0
        Log.e("111", "3getSuggestedMinimumWidth......." + getSuggestedMinimumWidth());//0

        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);

        Log.e("111", ".bitmap.getHeight......." + bitmap.getHeight());//300
        Log.e("111", ".bitmap.getWidth........" + bitmap.getWidth());//300

        bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        int bSize = Math.min(bitmap.getWidth(), bitmap.getHeight());
        float scale = mWidth * 1.0f / bSize;

        mMatrix.setScale(scale, scale);
        bitmapShader.setLocalMatrix(mMatrix);
        mPaint.setShader(bitmapShader);
        Log.e("111", "===drawCircle==mRadius=" + mRadius);//540
        canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
    }
}

activity 用了NoActionBar的style

    

为什么getSuggestedMinimumHeight、Width打印为零

我们修改布局:添加 android:minHeight=”100dp”和android:minWidth=”100dp”


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#e5e5e5"
    android:orientation="vertical">

    <com.android.imooc.RoundImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#6e6e6e"
        android:minHeight="100dp"
        android:minWidth="100dp" />
LinearLayout>

再次打印如下:

可见android:minHeight影响了getSuggestedMinimumHeight,
而且getSuggestedMinimumHeight默认为零。

display.getWidth()==1080
display.getHeight()==1812
densityDpi==480
density==3.0
statusHeight==72
displayHeight-statusHeight==1740
actionBarHeight==168
statusHeight+statusHeight==240
displayHeight-(statusHeight + actionBarHeight)==1572

.......onMeasure.......
0getHeight.......0
0getWidth.......0
0getMeasuredHeight.......1560
0getMeasuredWidth.......1080
0getSuggestedMinimumHeight.......300
0getSuggestedMinimumWidth.......300
.......onMeasure......................
0getHeight.......0
0getWidth.......0
0getMeasuredHeight.......1080
0getMeasuredWidth.......1080
0getSuggestedMinimumHeight.......300
0getSuggestedMinimumWidth.......300

.......onMeasure.......
0getHeight.......0
0getWidth.......0
0getMeasuredHeight.......1740
0getMeasuredWidth.......1080
0getSuggestedMinimumHeight.......300
0getSuggestedMinimumWidth.......300
.......onMeasure......................
0getHeight.......0
0getWidth.......0
0getMeasuredHeight.......1080
0getMeasuredWidth.......1080
0getSuggestedMinimumHeight.......300
0getSuggestedMinimumWidth.......300

.......onSizeChanged.......
1getHeight.......1080
1getWidth.......1080
1getMeasuredHeight.......1080
1getMeasuredWidth.......1080
1getSuggestedMinimumHeight.......300
1getSuggestedMinimumWidth.......300

.......onLayout.......
2getHeight.......1080
2getWidth.......1080
2getMeasuredHeight.......1080
2getMeasuredWidth.......1080
2getSuggestedMinimumHeight.......300
2getSuggestedMinimumWidth.......300

.......onDraw.......
3getHeight.......1080
3getWidth.......1080
3getMeasuredHeight.......1080
3getMeasuredWidth.......1080
3getSuggestedMinimumHeight.......300
3getSuggestedMinimumWidth.......300

.bitmap.getHeight.......300
.bitmap.getWidth........300
===drawCircle==mRadius=540

onLayout、layout

Android的onLayout、layout方法讲解
onLayout方法是ViewGroup中子View的布局方法,用于放置子View的位置。放置子View很简单,只需在重写onLayout方法,然后获取子View的实例,调用子View的layout方法实现布局。在实际开发中,一般要配合onMeasure测量方法一起使用。

@Override
protected abstract void onLayout(boolean changed,
            int l, int t, int r, int b);

该方法在ViewGroup中定义是抽象函数,继承该类必须实现onLayout方法,而ViewGroup的onMeasure并非必须重写的。View的放置都是根据一个矩形空间放置的,onLayout传下来的l,t,r,b分别是放置父控件的矩形可用空间(除去margin和padding的空间)的左上角的left、top以及右下角right、bottom值。

public void layout(int l, int t, int r, int b);

该方法是View的放置方法,在View类实现。调用该方法需要传入放置View的矩形空间左上角left、top值和右下角right、bottom值。这四个值是相对于父控件而言的。例如传入的是(10, 10, 100, 100),则该View在距离父控件的左上角位置(10, 10)处显示,显示的大小是宽高是90(参数r,b是相对左上角的),这有点像绝对布局。

使用方法:

public class MyViewGroup extends ViewGroup {

    // 子View的水平间隔
    private final static int padding = 20;

    public MyViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // TODO Auto-generated method stub

        // 动态获取子View实例
        for (int i = 0, size = getChildCount(); i < size; i++) {
            View view = getChildAt(i);
            // 放置子View,宽高都是100
            view.layout(l, t, l + 100, t + 100);
            l += 100 + padding;
        }

    }

}

自己的测试:


public class CircleProgressView extends View {

    public CircleProgressView(Context context, AttributeSet attrs,
                              int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        int measureHeight=getMeasuredHeight();
        int measureWidth=getMeasuredWidth();
        int height=getHeight();
        int width=getWidth();
        Log.e(TAG, "333onMeasure: measureHeight=="+measureHeight+"--measureWidth=="+measureWidth+"--height=="+height+"--width=="+width );
    }

    public CircleProgressView(Context context, AttributeSet attrs) {
        super(context, attrs);
        int measureHeight=getMeasuredHeight();
        int measureWidth=getMeasuredWidth();
        int height=getHeight();
        int width=getWidth();
        int left=getLeft();
        int right=getRight();
        Log.e(TAG, "444构造方法: measureHeight=="+measureHeight+"--measureWidth=="
                +measureWidth+"--height=="+height+"--width=="+width +"--left=="+left+"--right=="+right);
    }

    public CircleProgressView(Context context) {
        super(context);
        int measureHeight=getMeasuredHeight();
        int measureWidth=getMeasuredWidth();
        int height=getHeight();
        int width=getWidth();
        Log.e(TAG, "555onMeasure: measureHeight=="+measureHeight+"--measureWidth=="+measureWidth+"--height=="+height+"--width=="+width );
    }

    @Override
    protected void onMeasure(int widthMeasureSpec,
                             int heightMeasureSpec) {
        //widthMeasureSpec和heightMeasureSpec不是一般的尺寸数值,而是将模式和尺寸组合在一起的数值。
        mMeasureWidth = MeasureSpec.getSize(widthMeasureSpec);//测量后的宽度
        mMeasureHeigth = MeasureSpec.getSize(heightMeasureSpec);//测量后的高度
        setMeasuredDimension(mMeasureWidth, mMeasureHeigth);
        int measureHeight=getMeasuredHeight();
        int measureWidth=getMeasuredWidth();
        int height=getHeight();
        int width=getWidth();
        int left=getLeft();
        int right=getRight();
        Log.e(TAG, "1111onMeasure: measureHeight=="+measureHeight+"--measureWidth=="
                +measureWidth+"--height=="+height+"--width=="+width +"--left=="+left+"--right=="+right);
        initView();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 绘制圆
        canvas.drawCircle(mCircleXY, mCircleXY, mRadius, mCirclePaint);//圆心、半径
        // 绘制弧线
        canvas.drawArc(mArcRectF, 270, mSweepAngle, false, mArcPaint);//起始角度、扫过角度、不用中心
        // 绘制文字
        canvas.drawText(mShowText, 0, mShowText.length(),
                mCircleXY, mCircleXY + (mShowTextSize / 4), mTextPaint);//文本,文本的起始与终止,文本位置(要看对齐方式)

        int measureHeight=getMeasuredHeight();
        int measureWidth=getMeasuredWidth();
        int height=getHeight();
        int width=getWidth();
        int left=getLeft();
        int right=getRight();
        Log.e(TAG, "2222onDraw: measureHeight=="+measureHeight+"--measureWidth=="
                +measureWidth+"--height=="+height+"--width=="+width +"--left=="+left+"--right=="+right);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        int measureHeight=getMeasuredHeight();
        int measureWidth=getMeasuredWidth();
        int height=getHeight();
        int width=getWidth();
        Log.e(TAG, "666onLayout: measureHeight=="+measureHeight+"--measureWidth=="
                +measureWidth+"--height=="+height+"--width=="+width +"--left=="+left+"--right=="+right);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        int measureHeight=getMeasuredHeight();
        int measureWidth=getMeasuredWidth();
        int height=getHeight();
        int width=getWidth();
        int left=getLeft();
        int right=getRight();
        Log.e(TAG, "777onSizeChanged: measureHeight=="+measureHeight+"--measureWidth=="
                +measureWidth+"--height=="+height+"--width=="+width +"--left=="+left+"--right=="+right);
    }

    private void initView() {
        float length = 0;
        if (mMeasureHeigth >= mMeasureWidth) {
            length = mMeasureWidth;
        } else {
            length = mMeasureHeigth;
        }

        mCircleXY = length / 2;
        mRadius = (float) (length * 0.5 / 2);
        mCirclePaint = new Paint();
        mCirclePaint.setAntiAlias(true);
        mCirclePaint.setColor(getResources().getColor(
                android.R.color.holo_blue_bright));

        mArcRectF = new RectF(
                (float) (length * 0.1),
                (float) (length * 0.1),
                (float) (length * 0.9),
                (float) (length * 0.9));
        mSweepAngle = (mSweepValue / 100f) * 360f;
        mArcPaint = new Paint();
        mArcPaint.setAntiAlias(true);
        mArcPaint.setColor(getResources().getColor(
                android.R.color.holo_blue_bright));
        mArcPaint.setStrokeWidth((float) (length * 0.1));
        mArcPaint.setStyle(Style.STROKE);//描边

        mShowText = setShowText();
        mShowTextSize = setShowTextSize();
        mTextPaint = new Paint();
        mTextPaint.setTextSize(mShowTextSize);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
    }

}

log结果:

02-07 10:07:34.930 13920-13920/com.imooc.systemwidget E/ContentValues: 
444构造方法:
measureHeight==0--measureWidth==0--
height==0--width==0--
left==0--right==0

02-07 10:07:34.955 13920-13920/com.imooc.systemwidget E/ContentValues: 
1111onMeasure:
measureHeight==1692--measureWidth==1080--
height==0--width==0--
left==0--right==0

02-07 10:07:35.005 13920-13920/com.imooc.systemwidget E/ContentValues: 
1111onMeasure: measureHeight==1692--measureWidth==1080--
height==0--width==0--
left==0--right==0

02-07 10:07:35.006 13920-13920/com.imooc.systemwidget E/ContentValues: 
777onSizeChanged: measureHeight==1692--measureWidth==1080--
height==1692--width==1080--
left==0--right==1080

02-07 10:07:35.006 13920-13920/com.imooc.systemwidget E/ContentValues: 
666onLayout: measureHeight==1692--measureWidth==1080--
height==1692--width==1080--
left==0--right==1080

02-07 10:07:35.023 13920-13920/com.imooc.systemwidget E/ContentValues: 
2222onDraw: measureHeight==1692--measureWidth==1080--
height==1692--width==1080--
left==0--right==1080

02-07 10:07:35.092 13920-13920/com.imooc.systemwidget E/ContentValues: 
1111onMeasure: measureHeight==1692--measureWidth==1080--
height==1692--width==1080--
left==0--right==1080

02-07 10:07:35.094 13920-13920/com.imooc.systemwidget E/ContentValues: 
666onLayout: measureHeight==1692--measureWidth==1080--
height==1692--width==1080--
left==0--right==1080

02-07 10:07:35.095 13920-13920/com.imooc.systemwidget E/ContentValues: 
2222onDraw: measureHeight==1692--measureWidth==1080--
height==1692--width==1080--
left==0--right==1080

结论:

1、调用顺序应该是
构造函数——->onMeasure——->onSizeChanged——->onLayout——->onDraw——-> onMeasure——->onLayout——->onDraw
2、getMeasuredWidth()在onmeasure方法中可获取
getWidth()在onSizeChanged、onLayout、onDraw等方法中即可获取。
3、getWidth()=getRight()-getLeft()
4、当getMeasuredWidth()和getWidth()都有值的时候,两者相等。

参考

Android群英传

Android艺术探索

你可能感兴趣的:(自定义view,自定义view)