android开发艺术学习笔记--自定义View

481 阅读3分钟

理解View

ViewRoot

viewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程(measure、layout、draw是通过ViewRoot来完成的)。

DecorView是一个FrameLayout,view层的事件都是先经过DecorGView再传递给view。


measure、layout和draw的作用

  1. measure用来测量view的宽和高。

  2. layout用来确定view在父容器中放置的位置。

  3. draw负责将view绘制在屏幕上。


view的绘制过程是从ViewRoot的performTraversals开始的。

Measure

类的measure过程由measure方法来完成,measure会调用view的onMeasure方法。在onMeasure方法中会调用getDefault方法对设置view的宽高的测量值。通常的是.AT_MOST和.EXCATLY两种情况,getDefault返回measureSpec的specSize(某些大小的参数)。还有一种情况为UNSPECIFIED,一般用于系统内部的测量过程,则设定的值为android:minWidth(无背景)或android:minWidth和背景宽度之间较大的一个(有背景)。

为什么直接继承View 的自定义控件需要重写onMeasure方法并设置warp_content时的自身大小

如果不重写,在布局中使用wrap_content就相当于使用match_parent。

由图片上的代码可以知道MOST和EXACTLY都是返回一样的值。解决这个问题的方法如下:

即在AT_MOST的情况下给它设定一个默认的值。

LinearLayout的measure过程

在测量过程中,会通过判断LinearLayout的排列方式而选择测量方法,下面以垂直排列为例。

在MeasureVertical中,系统会遍历子元素并对每个子元素执行MeasureChildBeforeLayout,这个方法会调用measure,这样就使得各个子元素依次进入measure过程。系统还会通过mTotalLength这个变量来储存LinearLayout在垂直方向的初步高度,决定这个变量的因素有子元素的高度和竖直方向上的margin。

获取我们设置的view的方法

首先获得Viewgroup

ViewGroup content = findViewById(R.android.id.content);

Viewgroup是view的集合。content.getChildAt(0)就是设置在setContentView的view。


自定义view

自定义view的分类

  • 继承View重写onDraw
  • 继承ViewGroup派生特殊的Layout
  • 继承特定的View(比如TextView)
  • 继承特定的ViewGroup(比如LinearLayout)

简单的自定义View示例

功能:在一块屏幕上显示一个圆。

由于功能比较简单,所以只需要在创建一个类后继承View再重写它的OnDraw()方法。

布局
如上图所示(继承书写构造方法时加上super是想生成父类对象)

下图是运行效果

此时的CircleView由于margin属性是父容器控制的,所以不需要特殊处理也可以生效。但是warp_content(由前面可知它在处理之前效果和match_parent一样)和padding就需要自己处理。

//处理后的ondrow代码(用于padding)
 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int paddingLeft =getPaddingLeft();
        int paddingRight =getPaddingRight();
        int paddingTop =getPaddingTop();
        int paddingBottom =getPaddingBottom();
        int width =getWidth()-paddingLeft-paddingRight;
        int heigth=getHeight()-paddingBottom-paddingTop;
        int radius = Math.min(width,heigth)/2;
        canvas.drawCircle(paddingLeft+width/2,paddingTop+heigth/2,radius,mPaint);
    }

warp_content的设置在上面有写。

为自定义View添加自定义属性

  1. 在values目录下面创建自定义属性XML。比如attrs.xml(文件名并没有什么限制)。
  2. 在文件中定义属性。格式如下
    在这里对name=CircleView的view声明了一个叫circle_color的属性。类型为color类型。
  3. 在View的构造方法中解析自定义属性的值并做相应的处理。
    即用一个能保存所有属性值的变量类型TypeArray将所有的ciecleview所有的属性值读取出来。再用getcolor将对应的color类型的CircleView_circle_color(id为R.styleable.CircleView_circle_color)读取出来。最后一个参数为默认值。解析完后使用recycle实现资源回收。之后直接在layout中使用即可。
    使用前要加schemas声明

xmlns:app=http://schemas.android.com/apk/res-auto

app是自定义属性的前缀