Android群英传知识点回顾——第三章:Android控件架构与自定义控件详解


  • 3.1 Android控件架构
  • 3.2 View的测量
  • 3.3 View的绘制
  • 3.4 ViewGroup的测量
  • 3.5 ViewGroup的绘制
  • 3.6 自定义View
    • 3.6.1 对现有的空间进行拓展
    • 3.6.2 创建复合控件
    • 3.6.3 重写View来实现全新的空间
  • 3.7 自定义ViewGroup
  • 3.8 事件拦截机制分析

控件大致非为两类:

  • view控件:视图控件
  • viewGroup控件:包含多个View控件,并管理其包含的View控件
  • 两者之间的关系:上层控件负责下层子控件的测量与绘制,并传递交互事件

UI界面架构:

  • Activity都包含一个Window对象,通常由PhoneWindow来实现
  • PhoneWindow将一个DecorView设置为整个应用窗口的根View
    • DecorView为整个Window界面的最顶层View
    • DecorView只有一个子元素为LinearLayout,代表整个Window界面,包含通知栏,标题栏,内容显示栏三块区域
    • LinearLayout里有两个FrameLayout子元素:
      • 标题栏显示界面。只有一个TextView显示应用的名称
      • 内容栏显示界面。就是setContentView()方法载入的布局界面

MeasureSpec类:32位的int值,高2位为测量模式,低30位为测量大小

MeasureSpec模式:

  • EXACTLY:精确模式 ,当控件的layout_width属性或layout_height属性指定为具体值,控件大小也是该具体值
  • AT_MOST:最大值模式,当控件layout_width属性或layout_height属性指定为warp_content时,控件的尺寸不要超过父控件允许的最大尺寸
  • UNSPECIFIED:未指定模式,控件要多大就多大,通常情况下再绘制自定义View中才会使用

View类默认的onMeasure()方法只支持EXACTLY模式,View需要支持warp_content属性,那么就必须重写onMeasure()方法,来制定warp_content的大小

下面我们通过一个简单的实例,演示如何进行View的测量,首先,需要重写onMeasure()方法:

可以发现,onMeasure方法调用了父类的onMeasure方法,代码跟踪父类onMeasure方法

可以发现,系统最终会调用setMeasuredDimension(int measuredWidth,int measuredHeight)方法将测量后的宽高值设置进去,我们调用自定义的measureWidth()方法和measureHeight()方法,分别对宽高进行重新定义

下面以measureWidth()方法为例:

第一步:从MeasureSpec对象中提取出具体的测量模式和大小

第二步:通过不同的测量模式给出不同的测量值:

  • EXACTLY:使用指定的specSize即可
  • AT_MOST:取出我们指定的大小和SpecSize的最小值
  • UNSPECIFIED:200px

下面这段代码基本上可以作为模板代码:

可以发现,当指定warp_content属性时,View就获得一个默认值200px

当测量好了一个View之后,我们通过重写View类中的onDraw()方法来绘图,要想绘制相应的图像,就必须在Canvas上进行绘制

Canvas就像是一个画板,我们传进去一个bitmap,通过这个bitmap创建的Canvas画布紧紧联系在一起,这个过程我们称之为装载画布,这个bitmap用来存储所有绘制在Canvas上的像素信息,所以当你在后面调用所有的Canvas.drawxxx方法都会发生在这个bitmap上

ViewGroup在测量时通过遍历所有子View,从而调用子View的Measure方法来获得每一个子View的结果

ViewGroup测量完毕后,通常会去重写onLayout()方法来控制其子View显示位置的逻辑

ViewGroup通常不需要绘制,如果不是指定ViewGroup的背景颜色,那么ViewGroup的onDraw()方法都不会被调用,但是,ViewGroup会使用dispatchDraw()方法来绘制子View

在View中通常有以下一些重要的回调方法:

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

通常情况下,有以下三种方法来实现自定义的控件:

  • 对现有的控件进行拓展
  • 通过组合来实现新的控件
  • 重写View来实现全新的控件

3.6.1 对现有的控件进行拓展

  • 自定义修改TextView……见经典代码回顾,案例一
  • 闪动的文字效果……见经典代码回顾,案例二

3.6.2 创建复合控件

  • 自定义ToolBar的实现……见经典代码回顾,案例三

3.6.3 重写View来实现全新的控件

  • 弧线展示图……见经典代码回顾,案例四
  • 音频条形图……见经典代码回顾,案例五
  • 自定义ViewGroup,仿ScrollView……见经典代码回顾,案例六

事件拦截机制三个重要方法

  • dispatchTouchEvent():分发事件
  • onInterceptTouchEvent():拦截事件
  • onTouchEvent():处理事件

举一个例子说明事件分发机制:

  • ViewGroupA:处于视图最下层
  • ViewGroupB:处于视图中间层
  • View:处于视图最上层

正常的事件分发机制流程:

  • ViewGroupA dispatchTouchEvent
  • ViewGroupA onInterceptTouchEvent
  • ViewGroupB dispatchTouchEvent
  • ViewGroupB onInterceptTouchEvent
  • View dispatchTouchEvent
  • View onTouchEvent
  • ViewGroupB onTouchEvent
  • ViewGroupA onTouchEvent

若ViewGroupB的onInterceptTouchEvent()方法返回true的分发机制流程:

  • ViewGroupA dispatchTouchEvent
  • ViewGroupA onInterceptTouchEvent
  • ViewGroupB dispatchTouchEvent
  • ViewGroupB onInterceptTouchEvent
  • ViewGroupB onTouchEvent
  • ViewGroupA onTouchEvent

若View的onTouchEvent()方法返回true的分发机制流程:

  • ViewGroupA dispatchTouchEvent
  • ViewGroupA onInterceptTouchEvent
  • ViewGroupB dispatchTouchEvent
  • ViewGroupB onInterceptTouchEvent
  • View dispatchTouchEvent
  • View onTouchEvent

若ViewGroupB的onTouchEvent()方法返回true的分发机制流程:

  • ViewGroupA dispatchTouchEvent
  • ViewGroupA onInterceptTouchEvent
  • ViewGroupB dispatchTouchEvent
  • ViewGroupB onInterceptTouchEvent
  • View dispatchTouchEvent
  • View onTouchEvent
  • ViewGroupB onTouchEvent

简单的说dispatchTouchEvent()和onInterceptTouchEvent()是从下往上一层一层分发下去的,而onTouchEvent()是从上往下一层一层分发下去的




在values文件夹中创建一个attrs.xml文件来自定义属性

开始创建我们的ToolBar

在布局文件中使用


当用户不指定具体的比例值时,可以调用以下代码来设置相应的比例值



自定义的ScrollView没有系统自带的性能好,毕竟很多因素都没考虑到,这里只是适用于练手使用

在布局中使用

经典回顾源码下载

github:https://github.com/CSDNHensen/QunYingZhuang

原文地址:https://www.cnblogs.com/zhanglixina/p/9603816.html