View的测量与绘制

  View的测量:

 通过MeasureSpec这一个类, 就可以获取View的测量模式和View想要绘制的大小。 MeasureSpec类, 是一个32位的int值,前两位为测量的模式,测量的模式有三种(EXACTLY, AT_MOST, UNSPECIFIED)

  View类默认的测量view方式为onMeasure() 且只支持EXACTLY 模式, 所以如果在自定义控件的时候不重写onMeasure()方法的话, 就只能使用EXACTAL 模式。控件可以响应你指定的具体高度值或者是match_parent 属性。而如果要让自定义View 支持wrap_content 属性,那么必须重写onMeasure() 来指定wrap_content时的大小。

    通过MeasureSpec类可以获得View的测量模式与和View想测量的大小, 有了这些信息我们就可以控制view最后显示的大小。

public class TeachingView extends View {

    public TeachingView(Context context) {
        super(context);
    }

    public TeachingView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public TeachingView(Context context, AttributeSet attrs,
                        int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
    }
    //调用自动定义的measureWidth() 和 measureHeight() 对宽高进行重写定义。
    private int measureWidth(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = 200;
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    private int measureHeight(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = 200;
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.GRAY);
        int width = getWidth();
        int height = getHeight();
        Log.d("yy", "width : " + width + " height : " + height);
    }
}

  第一种条件,宽高值都为400dp, 运行结果:

    

第二种条件:指定宽高属性为 match_parent, 运行结果:

  第三种条件:指定宽高为wrap_content 属性, 此时如果不重写onMeasure() 方法,那么系统就不知道该使用默认多大的尺寸。 因此会默认填充父布局, 所以需要重写onMeasure() 方法 指定wrap_content 属性下的默认大小, 运行结果:



View的绘制:

     当测量好了一个View 之后, 就可以简单地重写onDraw() 方法,并在Canvas对象上来绘制所需要的图形。 2D绘图API 必须使用到 Canvas 对象, 使用Paint 就可以在对象上做画了。通常需要通过继承View 并重写它的onDraw() 方法来绘图。
 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.GRAY);
        int width = getWidth();
        int height = getHeight();
        Log.d("yy", "width : " + width + " height : " + height);
    }

   完成view的测量后, 重写onDraw() 方法,此方法中的参数即为Canvas, 并在Canvas对象上绘制图像 Canvas对象时2D绘图必须使用到的API。在onDraw()中使用这个对象就可以进行绘图, 但在其他地方, 通常需要使用代码创建一个Canvas对象:

Canvas canvas = new Canvas(bitmap);

 这个过程叫做装载画布, 这个bitmap用来存储所有绘制在Canvas上的像素信息。当通过这种方式创建了Canvas对象后, 后面所有的Canvas.DrawXXXX方法都发生在这个bitmap上。如果在View类的onDraw() 方法中通过下面的代码,可以了解到Canvas 和 Bitmap直接的关系。 首先在onDraw() 方法绘制两个bitmap

canvas.drawBitmap(bitmap1, 0, 0, null);
canvas.drawBitmap(bitmap2, 0, 0, null);

对于bitmap2 将它装载到另一个对象中:

Canvas mCanvas = new Canvas(bitmap2);

  那么在mCanvas上绘制view时:

mCanvas.drawxxx

 通过mCanvas将绘制的效果作用在了bitmap2上, 这是因为bitmap2承载了在mCanvas上进行的绘图操作,没有将图形直接绘制在onDraw() 方法指定的画布上, 而是通过改变bitmap, 使得view 进行的重绘, 从而显示改变之后的bitmap。通过改变bitmap,然后让view重绘, 从而显示改变后的bitmap 虽然使用了Canvas 的绘图API, 但其实并没有将图形直接绘制在onDraw() 方法指定的那块画布上。

--------------------------------------------------------------------------------------------------------------









猜你喜欢

转载自blog.csdn.net/qq_41405257/article/details/80649170