Android群英传读书笔记(第三章)

上一章

第三章终于进入开发的正题了!本章主要介绍自定义控件:

1.控件分为两类:ViewViewgroup,通过ViewGroup整个界面形成一个树形结构,并且ViewGroup负责对子View的测量与绘制以及传递交互事件。

2.Activity包含一个Window对象,Window对象又将一个DecorView设置为整个应用的根View。这里所有View的监听事件都通过WindowManagerService来接收,并通过Activity对象来回调onClickListenerDecorView在显示上分为TitleViewContentView两部分。可以通过如下代码获得ContentView

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

3.View的测量在onMeasure中进行,系统提供了MeasureSpec类,是一个32位的int值,其高2位为测量模式,低30位为测量的大小。测量模式有以下三种:

  • EXACTLY:精确模式,当控件指定精确值(例如android:layout_width="50dp")或者指定为match_parent属性时系统使用该模式。
  • AT_MOST:最大值模式,指定wrap_content时系统使用该属性,View类默认只支持EXACTLY,如果想使用wrap_content需自己在onMeasure中实现。
  • UNSPECIFIED:自定义模式,View想多大就多大,通常在绘制自定义View的时候才使用。

下面是onMeasure的事例代码:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);// 获取宽度模式
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);// 获取宽度值
    int width = 0;
    if (widthMode == MeasureSpec.EXACTLY) {
        width = widthSize;
    } else {
        width = 200;// 自定义的默认wrap_content值
        if (widthMode == MeasureSpec.AT_MOST) {
                width = Math.min(widthSize, width);
        }

    }
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);// 获取高度模式
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);// 获取高度值
    int height = 0;
    if (heightMode == MeasureSpec.EXACTLY) {
        height = heightSize;
    } else {
        height = 200;// 自定义的默认wrap_content值
        if (heightMode == MeasureSpec.AT_MOST) {
            height = Math.min(heightSize, height);
        }
    }
    setMeasuredDimension(width, height);// 最终将测量的值传入该方法完成测量
}

4.View的绘制是通过onDraw方法实现的,具体是通过对onDraw方法中canvas参数操作执行绘图。在其他地方,则需要自己创建canvas对象,创建时需传入一个bitmap对象, bitmap是用来保存Canvas.drawXXX绘制的像素信息的,通过这些绘图操作改变的实际上就是bitmap对象而不是canvas

5.当ViewGroup的大小为wrap_content时,它就会遍历所有子View,并调用其Measure方法获得其大小,来决定自身的大小,而在其他模式下则通过指定值来设置自身的大小。然后当View测量完毕以后,ViewGroup会执行它的Layout方法,同样是遍历子View并调用其Layout方法来确定布局位置。在自定义ViewGroup时,通常会重写onLayout()方法来控制子View显示位置,若需支持wrap_content还需重写onMeasure()方法。ViewGroup通常情况下不需要绘制,但是会调用dispatchDraw()方法来绘制其子View,过程同样是遍历子View

6.自定义View时有一些比较重要的回调方法如下:

onFinishInflate();//从xml加载组件后回调
onSizeChanged();//组件大小改变时回调
onMeasure();//回调该方法进行测量
onLayout();//回调该方法来确定显示的位置
onTouchEvent();//监听到触摸事件回调

7.自定义View通常有三种情况:
(1)对现有控件进行拓展:
一般来说,会在onDraw()方法中对原生控件行为进行拓展

@Override
    protected void onDraw(Canvas canvas) {
        //在回调父类方法前,实现自己的逻辑
        super.onDraw(canvas);
        //在回调父类方法后,实现自己的逻辑
    }

(2)通过组合来实现新的控件:
这种方式通常需要继承一个合适的ViewGroup,再给它添加指定功能的控件,从而组合成新的复合控件。
(3)重写View来实现全新的控件:
当Android系统原生的控件无法满足我们的需求时,就需要创建一个全新的自定义View了。通常需要继承View类,并重写它的onDraw()onMeasure()等方法实现绘制逻辑,同时通过重写onTouchEvent()等触控事件来实现交互逻辑,还可以引入自定义属性,丰富自定义View的可定制性。

8.本章较为浅显的分析了下事件传递的机制。当ViewGroup接收到事件,通过调用dispatchTouchEvent(),由这个方法再调用onInterceptTouchEvent()方法来判断是否要拦截事件,如果返回true则拦截将事件交给onTouchEvent处理,返回false则继续向下传递。当View在接受到事件时,通过调用dispatchTouchEvent(),由此方法再调用onTouchEvent方法,如果返回true则拦截事件自己处理,如果返回false则将事件向上传递回ViewGroup并且调用其onTouchEvent方法继续做判断。

本章中用代码例举了很多自定义View,但由于本人对一些系统绘制的api尚不熟悉,等以后弄懂了再补充

下一章

你可能感兴趣的:(Android群英传读书笔记(第三章))