View的学习记录(一)

本篇目的

探讨View的绘制过程以及分析View在绘制过程中我们可以参与改造的地方,为自定义View做基础

View的调用

View视图树

ViewParent
ViewGroup
ViewGroup
View
View
View
View

Activity中视图的结构

在Activity中,通过setContentView()来设置一个layout布局,在调用后,布局内容才会显示出来
DecorView作为View的根布局来管理所有的View,将视图呈现在了phoneWindow中,DecorView负责View的所有监听事件,通过WindowManagerService来接收,通过Activity对象来回调onClickListener

包含
设置根View
包含
包含
ID为container
ID为content
Activity
PhoneWindow
DecorView
TitleView
ContentView
appBar
Framelayout

通过设置requestWindowFeature(Window.Feature.NO_TITLE)来取消appBar
在代码中,setContentView()之后,ActivityManagerService会回调onResume()方法,此时,会把DecorView添加到PhoneWindow中,完成界面绘制.

View的测量

onMeasure()

所有的View都自带一个onMeasure()函数,参数是MeasureSpec类
在onMeasure()方法中,最后,都是通过setMeasuredDimension()方法来保存测量的结果
下面是ViewGroup的测量源码

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

我们可以通过在onMeasure()方法的最后,手动调用setMeasuredDimension()来保存我们自己测量的结果

MeasurSpec类

MeasurSpec是一个设计短小精悍的类
自身是一个32位int值
高2位位测量模式,可以MeasureSpec.getMode()获取
低30位为测量的大小,在计算中,使用的是位计算,为了提高计算速率,可以通过MeasureSpec.getSize()获取

测量模式分三类

  • EXACTLY
    精确测量模式,对应我们指定控件大小,例如"100dp"或者match_parent这两种情况
  • AT_MOST
    最大值模式,对应指定控件大小为wrap_content时,随控件内容的变化而变化
    还要不超过父控件允许的最大尺寸即可.
  • UNSPECIFIED
    自定义模式
    不会指定大小测量模式,View想多大就多大,通常制作自定义View时使用
    View类默认的onMeasure()方法只支持EXACTLY模式,自定义View继承自View时,就需要重写onMeasure()方法,拓展对wrap_content模式的支持(否则,默认match_parent)

重写onMeasure()

	int MaxSize=200;
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
    }

    private int measureHeight(int heightMeasureSpec) {
        int result=0;
        int specMode=MeasureSpec.getMode(heightMeasureSpec);
        int specSize=MeasureSpec.getSize(heightMeasureSpec);
        if (specMode==MeasureSpec.EXACTLY){
            result=specSize;
        }else {
            result=MaxSize;
            if (specMode==MeasureSpec.AT_MOST){
                result=Math.min(result,specSize);
            }
        }
        return result;
    }

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

View的绘制

测量好View之后,会调用onDraw()方法来绘制View.我们可以重写onDraw()方法,来实现我们想要额外实现的效果
这里就需要了解下系统2D绘图API
在onDraw()方法中,会提供一个Canvas对象,控件就绘制在Canvas上.
画布的创建是通过Bitmap对象,

Canvas canvas=new Canvas(bitmap);

bitmap用来承载canvas上所有的像素信息.

在onDraw(canvas)中,在canva上绘制背景.然后通过Canvas.drawBitmap()来绘制两张画布,在第二张画布上来绘制我们的View,通过改变bitmap来引起View重绘,从而显示改变后的Vbitmap

canVas相当于画布,绘制就需要画笔,系统提供给我们的画笔是Paint及其子类TextPaint等

ViewGrouop的测量和绘制

测量

ViewGroup会管理其子View
当ViewGroup的模式为wrap_content时,就需要遍历所有子View,调用子View自身的Measure()方法后,获得所有子View的大小,来决定自己的大小,其他模式时,会通过具体的指定值来设置自身大小

放置

测量后,需要把子view放置在合适的位置上,就是View的layout过程,同样是遍历子View,调用子View的layout方法,来指定子View的具体位置

绘制

ViewGroup通常情况下不需要绘制,如果背景指定了颜色或者图层,才会触发ViewGroup自身的onDraw()方法.
一般情况下,ViewGroup会调用dispatchDraw()方法,来绘制子View,同样是遍历所有子View,调用子View的Draw()方法.

资料来自于<Android群英传>

猜你喜欢

转载自blog.csdn.net/RungBy/article/details/83345147