欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

View的测量与绘制

程序员文章站 2022-05-24 20:52:09
...

  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, 运行结果:

    View的测量与绘制

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

View的测量与绘制

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

View的测量与绘制

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() 方法指定的那块画布上。

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









相关标签: View