android开发艺术(三)之View工作原理及其自定义

1.基本概念

1.1 ViewRoot

ViewRoot对应于ViewRootlmpl类,它是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完毕后,会将DecorView 添加到PhoneWindow 中,同时会创建ViewRootlmpl 对象,并将ViewRootlmpl对象和DecorView建立关联

将DecorView添加到窗口视图
    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
获得ViewRootImpl对象root
    root = new ViewRootImpl(view.getContext(), display);
将传进来的参数DecorView设置到root中
    root.setView(view, wparams, panelParentView);

View的绘制流程是从ViewRoot的performTraversals 方法开始的,它经过measure、layout和draw三个过程才能最终将一个View绘制出来,其中measure用来测量View的宽和高,layout用来确定View在父容器中的放置位置,而draw则负责将View绘制在屏幕上。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2 DecorView

DecorView是顶级View,包含了一个竖直方向的LinearLayout,上面是标题栏,下面是内容栏,在Activity中通过setContentView进行设置。View层的事件都先经过DecorView,然后才传给我们的View。
获得content:ViewGroup content = findViewById(id为android.R.id.content)
设置View: Content.getChildAt(0);

1.3 MeasureSpec

很大程度上决定了一个View的尺寸规格,之所以说是很大程度上是因为这个过程还受父容器的影响,因为父容器影响View的MeasureSpec的创建过程。在测量过程中,系统会将View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,MeasureSpec 是一个32位int值,高2位SpecMode为测量模式,低30位SpecSize是测量模式下的规格大小;MeasureSpec通过将SpecMode和SpecSize 打包成一个int值来避免过多的内存分配,为了方便操作,其提供了打包和解包方法。一组SpecMode和SpecSize可以打包为一个MeasureSpec,而一个MeasureSpec可以通过解包的形式来得出其原始的SpecMode和SpecSize;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.View的工作流程

2.1 View的measure过程:Measure()方法—onMeasure()—getDefaultSize()

  1. 直接继承View的自定义控件需要重写onMeasure并且设置wrap_content时的自身大小,否则在布局中使用wrap_content等同于match_parent
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec,heightMeasureSpec);
        
		int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        //分析模式,根据不同的模式来设置
        if(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(mWidth,mHeight);
        }else if(widthSpecMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(mWidth,heightSpecSize);
        }else if(heightSpecMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(widthSpecSize,mHeight);
        }
    }
  1. ViewGroup的measure流程
    自己的measure()—子元素的measure()—measure方法中调用的是measureChildren( ),measureChild方法取出子元素的LayoutParams 然后通过getChildMeasureSpec创建子元素的MeasureSpec,传递给View
    自定义View Measure过程
    在这里插入图片描述

2.2 layout过程

ViewGroup用来确定子元素的位子,ViewGroup确定之后在onLayout中会遍历所有的子元素并调用其layout方法,在layout中onLayout又被调用
流程:首先设定View的四个顶点,View在父容器位置确定,接着onLayout被调用,确定子元素的位置。onLayout方法和具体布局有关 View和ViewGroup都没有实现。在View的默认实现中,View的测量宽高和最终宽高是相等的,只不过测量宽高形成于measure过程,而最终宽高形成于layout过程

2.3 draw过程

Draw过程将View绘制到屏幕上,步骤如下(从顶级View到view遍历去做):

  1. 绘制背景:background.draw(canvas);
  2. 绘制自己:onDraw();
  3. 绘制children:dispatchDraw;
  4. 绘制装饰:onDrawScrollBars

3.自定义View

分类:

  1. 继承view重写onDraw方法
    重写onDraw需要自己支持wrap_content ,并且padding也需要处理
  2. 继承ViewGroup派生特殊的Layout
    主要用于实现自定义布局
  3. 继承特定的View(比如TextView)
    一般用于扩展已有的View的功能 不需要处理wrap_content 和padding
  4. 继承特殊的ViewGroup(比如LinearLayout)

须知:

  1. 让View支持wrap_content,因为直接维承View或者ViewGroup的控件,如不在onMeasure中对wrap_ content做特殊处理。那么使用无法达到预期效果
  2. 让你的View支持padding,直接继承View的控件默认padding属性无法起作用,所以要在draw方法中处理padding;直接维承ViewGroup的控件需要在onMeasure和onLayout中考虑padding和子元素的margin对其造成的影响,否则将导致padding和子元素的margin失效。
  3. 尽量不要在View中使用Handler
    因为View内部提供了post系列的方法,可替代Handler,如果明确发送消息的话可以用handler
  4. View中证有线程或动画,需要及时停止,不处理有可能会造成内存泄露,View不可见时也要停止线程和动画;包含View的Activity启动时, View的onAttachedToWindow方法会被调用;当包含此View的Activity取出或者当前View被remove时,View的onDetachedFromWindow方法会被调用
  5. 滑动嵌套, 处理好

实例

1.继承View 重写onDraw方法,需要处理wrap_content和padding

在这里插入图片描述
2.提供自定义属性:
2.1 values目录下创建自定义属性的XML
在这里插入图片描述
2.2 在View的构造方法中解析自定义属性的值并进行处理
在这里插入图片描述
2.3 布局文件中使用自定义属性

添加声明     xmlns:app="http://schemas.android.com/apk/res-auto"
使用属性     app:circle_color="@color/light_green"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值