《Android群英传》读书笔记(1)第三章:Android控件与自定义控件详解

一、Android控件架构

1.在Android中控件大致分为两类:ViewGroup和View,ViewGroup作为父控件可以包含多个View控件,这就是我们常说的控件树。上层控件负责下层控件的测量和绘制,并传递交互事件。

2.Android界面架构图:每个Activity都包含一个Window对象,多数由PhoneWindow来实现。PhoneWindow将一个DecorView设置为整个应用窗口的根View。DecorView作为窗口界面的顶层视图,封装了一些操作窗口的通用方法。这里所有的View监听事件都通过WindowManagerService来进行接收。DecorView将屏幕分成两部分,一个是TitleView,一个是ContentView,ContentView是一个id为content的FrameLayout,activity_main.xml就是设置在这样一个FrameLayout里。requestWindowFeature()需要在setContentView()之前调用才能生效。当程序运行到setContentView()的方法后,ActivityManagerService会回调onResume()方法,此时系统才会把整个DecorView添加到PhoneWindow中。
《Android群英传》读书笔记(1)第三章:Android控件与自定义控件详解_第1张图片

3.View的测量:View的测量在onMeasure()方法中进行,MeasureSpec类来帮助我们测量View,MeasureSpec是一个32位的int值,其中高2位为测量模式,低30位为测量的大小,在计算中使用位运算可以提高并优化效率。
测量模式分为三种:
①EXACTLY(精确模式),当我们指定layout_width或layout_height为具体数值时或指定为match_parent时,使用的是该模式。
②AT_MOST(最大值模式),当我们指定layout_width或layout_height为wrap_content时,控件大小一般随着自控件或者控件的内容而变化,此时控件的尺寸只要不超过父控件的最大尺寸即可。
③UNSPECIFIED,它不指定其大小测量模式,通常在绘制自定义view时使用。
View类默认的onMeasure()方法只支持EXACTLY模式,自定义控件时需要重写该方法来支持其他模式。
onMeasure()方法实现举例:

@Override
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec){
    int measuredWidth = measureWidth(widthMeasureSpec);
    int measuredHeight = measureHeight(heightMeasureSpec);
    setMeasureDimension(measuredWidth,measuredHeight);
}

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 {
        //否则为AT_MOST或UNSPECIFIED模式
        result = 100;
        if(specMode == MeasureSpec.AT_MOST){
            //具体情况要根据控件内容或子控件大小测量view
            result = Math.min(specSize ,result);
        }
    }
    return result;
}

private int measureHeight(int heightMeasureSpec){
    //和measureHeight()实现类似
    ...
}

4.view的绘制:view的绘制在onDraw(Canvas canvas)中进行,onDraw有一个参数canvas,即画布。当创建一个Canvas时:Canvas mCanvas = new Canvas(bitmap);需要传入一个bitmap对象,以后通过mCanvas绘制的内容都在bitmap上,这叫做装载画布。

5.ViewGroup的测量和绘制
ViewGroup的大小设置为wrap_content时,ViewGroup会遍历其子view,通过调用子view的Measure方法,获得所有的子view大小,来决定自己的大小。而在其他模式下则会根据具体的数值设定自己的大小。
当子view测量完毕就需要将子view放到合适的位置,这个过程就是view的layout过程。ViewGroup在执行layout时,同样是使用遍历来执行子view的layout方法,并制定其具体显示位置。
如果自定义ViewGroup,通常会去重写onLayout()方法来控制子view显示位置的逻辑。同样,如果需要支持wrap_content属性,还需要重写onMeasure()方法。这点与View是相同的。
ViewGroup通常是不需要绘制的,除非指定了其背景颜色,因此ViewGroup的onDraw方法都不会被调用。但是它会使用dispatchDraw()方法来绘制其字view,同样是遍历所有字view并调用子view的绘制方法来完成绘制工作。

6.自定义View
几个重要的回调方法:
onFinishInflate():从XML加载组件后回调。
onSizeChanged():组件大小改变时回调。
onMeasure():回调该方法来进行测量。
onLayout():回调该方法来确定显示的位置。
onTouchEvent():监听到触摸事件时的回调。

自定义view的三种方法:
对现有的控件进行拓展。
通过组合来实现新的控件。
重写view来实现全新的控件。

控件的宽高在onSizeChanged里获取。
使用 标签在values/attrs.xml中自定义控件属性,在java中通过TypedArray来获得自定义的属性,并记得使用完后要调用ta.recycle()来进行回收。
最好先定义属性,然后再在中进行引用,像这样:

<? xml version = "1.0" encoding = "utf-8" ?>
< resources >
     < attr name = "icon" format = "integer" />
     < declare-styleable name= "PreferenceHeader" >
        <!-- Identifier value for the header. -->
        < attr name= "id" format = "integer"/>
        < attr name= "icon" />
        <!-- The fragment that is displayed when the user selects this item. -->
    </declare-styleable >
    < declare-styleable name= "Preference" >
        < attr name= "icon" />
        <!-- The key to store the Preference value. -->
        < attr name= "key" format = "string" />
    </declare-styleable >
</ resources >

否则在编译时可能会遇到Attribute ** has already been defined的错误。或者更改冲突的属性名。
7.自定义ViewGroup
ViewGroup的存在目的就是为了管理子view,为其子view添加显示、响应的规则。因此自定义ViewGroup需要重写onMeasure()对子view进行测量,重写onLayout()确定子view的位置,重写onTouchEvent()增加响应时间。
方法getScrollY()、getScrollX()返回的是ViewGroup内容滑出屏幕的部分距离屏幕边缘的距离。例如ViewGroup中的内容向上移动了100像素,则getScrollY() = 100;即是移除去的部分的top距离屏幕上边缘的长度。

你可能感兴趣的:(android,读书笔记,控件)