Android群英传第三章笔记·Android控件架构与自定义控件详解

Android控件架构

  • Android中,控件大致被分为两类,即ViewGroup控件与View控件。ViewGroup控件作为父控件可以包含多个View控件,并管理其包含的View控件。
  • 通常在Activity中使用的findViewById()方法,就是在控件树中以树的深度优先遍历来查找对应元素。
    View树结构:
    Android群英传第三章笔记·Android控件架构与自定义控件详解_第1张图片
    UI界面架构图:
    Android群英传第三章笔记·Android控件架构与自定义控件详解_第2张图片

    1. PhoneWindow将一个DecorView设置为整个应用窗口的根View。
    2. DecorView将要显示的具体内容呈现在了PhoneWindow上,这里面的所有View的监听事件,都通过WindowManagerService来进行接收,并通过Activity对象来回调相应的onClickLisener。

      • 在代码中,程序在onCreate方法调用setContentView方法后,ActivityManagerService会回调onResume方法,此时系统才会把整个DecorView添加到PhoneWindow中,并让其显示出来,从而完成界面的绘制。

View的测量

  • Android在绘制View前,也必须对View进行测量,即告诉系统该画一个多大的View。这个过程在onMeasure方法中进行。
  • Android给我们提供了一个设计短小精悍却功能强大的类——MeasureSpec类,通过它来帮助我们测量View。MeasureSpec是一个32位的int值,其中高2位为测量的模式,低30位为测量的大小,在计算中使用位运算的原因是为了提高并优化效率。

    Android群英传第三章笔记·Android控件架构与自定义控件详解_第3张图片

  • onMeasure方法:父控件传递给子控件的“建议”尺寸大小,两个参数表示宽高大小及测量模式(MeasureSpec类介绍及使用详解)。如果是ViewGroup,且大小为Wrap_content时,则需要遍历所有子View测量他们的大小,然后设置自身的大小。
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec,heightMeasureSpec);
    }
  • setMeasureDimension方法:将测量后的尺寸传递给子控件,完成测量工作。
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasureDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
    }

     public static int measureWidth(int measureSpec) 
    {//根据父控件给的建议尺寸和测量模式,计算相应的尺寸
            int result = 0;             
             int specMode = MeasureSpec.getMode(measureSpec);            
             int specSize =  MeasureSpec.getSize(measureSpec);                       
             switch (specMode) 
             {        
                         case MeasureSpec.UNSPECIFIED:             
                             result = size;           
                                 break;                                     
                     case MeasureSpec.AT_MOST:  
                            result = 200;//指定view的大小
                             result = Math.min(result, specSize);            
                                 break;                                       
                     case MeasureSpec.EXACTLY:             
                             result = specSize;            
                                 break;         
                 }                                
             return result;    
     }
    

ViewGroup的子View位置设定

在ViewGrope及其子View完成测量之后,需要对子view进行放置位置的设定。在放置子view前,需要确定整个vewGroup的高度。然后在OnLayout方法中遍历调用子view的layout方法设定具体位置。

View的绘制

  • 过程:重写onDraw(),在Canvas对象上绘制图形。Canvas为系统2D绘图API必须要使用的对象。
  • canvas通常会带有一个bitmap参数,用于存储绘制在canvas上的像素信息。当创建了含有bitmap的canvas对象后,所有canvas.drawxxx方法都发生在这个bitmap上。值得注意的是,view的onDraw方法中会创建两个bitmap,并将第二个bitmap封装到另一个canvas对象中,只在第二个bitmap上进行绘图。
  • ViewGroup的绘制:如果没有设置背景色,不会调用onDraw方法,而是调用dispatchDraw方法绘制子View。原理也是遍历子view并调用其绘制方法。

自定义View

  • 自定义View通常需要重写onDraw方法来绘制自定义内容。如果设置了wrap_content属性,那么必须重写onMeasure方法测量大小。
  • View中有一些重要的回调方法:
    1. onFinishInflate():从XML加载组件后回调
    2. onSizeChanged():组件大小改变时回调
    3. onMeasure:测量
    4. onLayout():回调该方法来确定显示的位置
    5. onTouchEvent():监听到触摸事件回调
  • 实现自定义控件的方式

    • 对现有控件进行拓展
      @Override
      protected void onDraw(Canvas canvas) {
      //父类绘制之前,实现自己的效果
      super.onDraw(canvas);
      //父类绘制完成后,再实现自己的效果
    • 通过组合来实现新的控件
      1. 自定义属性
      2. 命名控件 介绍
    • 重写View来实现全新的控件 例子:音频条形图

    • 自定义view的一个例子

事件拦截机制

  • 几个重要的方法:dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent.
  • 从ViewGroup到view的事件分发和处理回调事件顺序如下(以ViewGroupA,ViewGroupB,view为例):
    ViewGroupA.dispatchTouchEvent –> ViewGroupA.onInterceptTouchEvent –> ViewGroupB.dispatchTouchEvent –> ViewGroupB.onInterceptTouchEvent –> view.dispatchTouchEvent –> view.onTouchEvent –> ViewGroupB.onTouchEvent –> ViewGroupA.onTouchEvent
    解释:
    1. dispatchTouchEvent方法用于分发事件,一般不用重写。onInterceptTouchEvent用来拦截TouchEvent,onTouchEvent用来处理TouchEvent。
    2. onInterceptTouchEvent返回true则拦截事件,不往下分发;onTouchEvent返回true则不往上回调。

你可能感兴趣的:(android)