Android view的测量及绘制

  讲真,自我感受,个人水平真的是渣的一匹,好多东西都只停留在知道和会用的阶段,也想去研究原理和底层的实现,但是一看到代码就懵逼了,而后就看不下去了,html

说本身不着急都是骗人的,我本身都不信,前两天买了本《Android 群英传》,江湖上都说这是一本初级过渡到中级不错的进阶书,因此准备看一下,才看了两天,今天android

看到了view的测量及绘制,还有自定义view(还没看完),学到什么就写篇博客吧,算是对本身所学的一个总结和记录吧,也能够督促本身,若是有讲的不对的地方或者canvas

有歧义的地方,欢迎你们吐槽批评我!app

  转载请注明出处:Android view的测量及绘制ide

  正文开始(手动鼓掌)布局

  view的测量:spa

  首先须要知道的是view的三种测量模式rest

  一、EXACTLY:精确值模式,当咱们对view的layout_width和layout_height属性指定具体的数值的时候,好比layout_width=“100dp”或者指定为match_parent时,系统code

进行测量的时候,使用的是这种模式。xml

  二、AT_MOST:最大值模式,当咱们对view的layout_width和layout_height属性指定为wrap_content时,即view随着内容的大小变化而变化,或viewgroup随着view的

大小变化而变化,这个时候系统进行测量的时候,使用的是这种模式。

  三、UNSPECIFIED:这个属性下不用指定其大小,通常在自定义view时才会使用(这种模式不是很理解,求指教)

  在对view进行测量的时候,须要重写onMeasure()方法,view默认的onMeasure()方法只支持EXACTLY模式,即指定具体的数值,因此在自定义view的时候必须重写

onMeasure(),这里留一个疑问:何时才会调用Measure方法进行测量?

 

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

 

  重写后点super.onMeasure(widthMeasureSpec, heightMeasureSpec);进去看一下源码,发现系统最终会调用这个方法:

 

setMeasuredDimension(int widthMeasureSpec,int heightMeasureSpec);

 

这个方法的做用是将咱们对view设置的宽和高设置进去,因此咱们最终重写的onMeasure()方法就是这个样子的,方法内的两个入参widthMeasureSpec和heightMeasureSpec

就是咱们在xml里引用这个view时设置的width和height,后面咱们须要根据这两个值进行判断,判断系统要根据什么测量模式进行测量。

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
    }

  前文一直在说系统的测量模式,那么咱们要怎么样才能获取到系统的测量模式呢?获取测量模式后还须要获取具体的测量大小

int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);

  单独对measureWidth(widthMeasureSpec)讲解一下,由于height和width是同样的。

 

    private int measureWidth(int widthMeasureSpec) {
        int widthResult = 0;
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);
        if (specMode == MeasureSpec.EXACTLY){
            widthResult = specSize;
        }else {
            widthResult = 400;
            if (specMode == MeasureSpec.AT_MOST){
                widthResult = Math.min(widthResult,specSize);
            }
        }
        return widthResult;
    }

 

  这部分的理解要联系到前面说的3种测量模式,若是是在EXACTLY if (specMode == MeasureSpec.EXACTLY) 这种模式下,咱们已经在xml里面设置好了具体的数值,因此最后返回的值就是specSize

若是是AT_MOSTUNSPECIFIED这两种测量模式下,咱们就须要给view一个默认的大小,由于若是没有给默认的大小的话,系统不知道view的大小,因此view或默认充满父布局。这里默认的大小是400,

你们会发现else里面还有一个if,对AT_MOST这种模式又进行了判断,这是由于在这种模式下,view不须要默认的大小,view的是根据内容的大小变换而变化的。

最后就是xml里面进行引用和效果展现了:

 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.funny.myapplication.MainActivity">

    <com.funny.myapplication.StudyView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="View"
        android:textSize="20sp"
        android:background="#ccf"
        android:gravity="center"/>
</RelativeLayout>

 

(1)android:layout_width="wrap_content"

(2)android:layout_width="match_parent"

(3)android:layout_width="400px"

 

  view的绘制:

  对view进行绘制须要重写onDraw()方法,onDraw()方法中会有一个canvas,能够把这个参数理解成画板,咱们最终会借用这个画板进行绘制。

有了画板,要想绘画的话固然还须要一支画笔paint。

    @Override
    protected void onDraw(Canvas canvas) {
        Paint paint1 = new Paint();
        paint1.setColor(getResources().getColor(R.color.colorAccent));
        paint1.setStyle(Paint.Style.FILL);
        Paint paint2 = new Paint();
        paint2.setColor(getResources().getColor(R.color.colorPrimary));
        paint2.setStyle(Paint.Style.FILL);
     //开始绘制
        canvas.drawOval(0,0,getMeasuredWidth(),getMeasuredHeight(),paint1);
        canvas.drawOval(0,0,getMeasuredWidth()-10,getMeasuredHeight()-10,paint2);
     //平移30个像素 canvas.translate(
30,0 ); super.onDraw(canvas); canvas.restore(); }

  接下讲解一下这段代码的功能,首先实例了两只画笔,而且对画笔设置了颜色和风格,接下来开始绘制,这里咱们画的是两个相互嵌套的椭圆,

canvas.drawOval(0,0,getMeasuredWidth(),getMeasuredHeight(),paint1);这个方法须要5个入参,分别是view相对于父布局的左、上、右、下的坐标,最后一个是进行绘制的画笔。

  系统给咱们提供的drawXXX方法有不少:

canvas绘制的经常使用方法有:
drawColor() 填充颜色
drawLine() 绘制线
drawLines() 绘制线条
drawOval() 绘制圆
drawPath() 绘制路径
drawPicture() 绘制图片
drawPoint() 绘制点
drawPoints() 绘制点
drawRGB() 填充颜色
drawRect() 绘制矩形
drawText() 绘制文本
drawTextOnPath() 在路径上绘制文本 

效果展现:

  viewGroup的测量:

  咱们知道viewGroup管理子view,那么viewGroup的大小,除设置指定大小外,是根据子view来决定的,viewGroup在测量时会遍历全部的子view,调用

子view的Measure方法来得到每个子view的测量结果,这样文章开始留下的疑问就解决了,意不意外~关于viewGroup的绘制,通常状况下若是不是指定

viewGroup的背景颜色,viewGroup的onDraw()方法不会被调用,可是viewGroup会使用dispatchDraw()方法来绘制其子view,过程一样是遍历全部的子view

,并调用子view的绘制方法来完成绘制。

 

  以上就是我对view的测量及绘制的所有理解,可能在不少人看来仍是处于初级阶段,没有怎么涉及到源码,可是仍是但愿能帮助到一些人吧,对我本身也是一点点进步嘛,若是你们有什么

好的理解,欢迎留言哈~~~酱!拜啦!

相关文章
相关标签/搜索