Android自定义View

本文深入探讨了Android自定义View的实现原理,包括View和ViewGroup的基本架构、测量与绘制流程,以及如何通过重写关键方法来自定义控件。此外,还介绍了事件拦截机制和自定义控件的三种常见实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

声明:本文为《Android群英传》读书笔记

Android自定义View

Android 控件架构

Android中,控件被分为两类:

  1. ViewGroup
  2. View

他们之间的关系是ViewGroup可以包含并管理多个View,下图是View树结构:
View树结构

再来看看Android界面的架构图:
UI界面架构图

通过上图,可以建立起这样一个标准视图树:
标准视图树

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

View的测量与绘制

View的测量(onMeasure())

Android中通过MeasureSpec类来帮助我们测量View:
MeasureSpec:32位int值,高2位为测量模式,低30位为测量大小。
测量模式:

  • EXACTLY

    精确模式(默认),控件大小指定为具体数值,比如android:layout_width="100dp",或者match_parent时,使用的就是此模式

  • AT_MOST

    最大值模式,指定为warp_content时,为此模式,只要不超过父控件允许的最大尺寸即可。

  • UNSPECIFIED

    不指定其大小测量模式,View想多大就多大,通常自定义View会用

重写onMeasure()最重要的工作是把测量的宽高值作为参数设置给setMeasureDimension()方法。

View的绘制(onDraw())

要想在Android的界面中绘制相应的图像,就必须在Canvas上进行绘制。

Canvas canvas = new Canvas(bitmpa);

创建一个Canvas对象时,一般传进一个bitmap对象,传进去的bitmap与通过这个bitmap创建的Canvas画布是紧紧联系在一起的,这个过程我们称之为装载画布。这个bitmap用来存储所有绘制在Canvas上的像素信息。虽然我们也使用了Canvas的绘制API,但其实并没有将图形直接绘制在onDraw()方法指定的那块画布上,而是通过改变bitmap,然后让View重绘,从而显示改变之后的bitmap。

ViewGroup的测量与绘制

ViewGroup的测量(onMeasure())

ViewGroup会去管理其子View,其中一个管理项目就是负责子View的显示大小。当ViewGroup的大小为warp_content时,ViewGroup就需要对子View进行遍历,以便获得所有子View的大小,从而来决定自己的大小。而在其他模式下则会通过具体的指定值来设置自身的大小。

ViewGourp在测量时通过遍历所有子View,从而调用子View的Measure方法来获得每一个子View的测量结果,前面所说的对View的测量,就是在这里进行的。

ViewGroup的布局(onLayout())

当子View测量完毕后,就需要将子View放到合适的位置,这个过程就是View的Layout过程。ViewGroup在执行Layout过程时,同样是使用遍历来调用子View的Layout方法,并指定其具体显示的位置,从而来决定其布局位置。

在自定义ViewGroup时,通常会去重写onLayout()方法来控制其子View显示位置的逻辑。同样,如果需要支持wrap_content属性,那么它还必须重写onMeasure()方法,这点与View是相同的。

ViewGroup的绘制(onDraw())

ViewGroup通常情况下不需要绘制,因为它本身就没有需要绘制的东西,如果不是指定了ViewGroup的背景颜色,那么ViewGroup的onDraw()方法都不会被调用。但是,ViewGroup会使用dispatchDraw()方法来绘制其子View,其过程同样是通过遍历所有子View,并调用子View的绘制方法来完成绘制工作。

自定义控件的三种方式

在自定义View时,我们通常会去重写onDraw()方法来绘制View的显示内容。如果该View还需要使用warp_content属性,那么还必须重写onMeasure()方法。另外,通过自定义attrs属性,还可以设置新的属性配置值。需要注意的是,当获取完所有的属性值后,需要调用TypedArray的recyle方法来完成资源的回收。

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

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

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

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

使用自定义的View与系统原生的View最大区别就是在申明控件时,需要指定完整的包名,而在引用自定义的属性时,需要使用自定义的xmlns名字。

创建一个自定义View,难点在于绘制控件和实现交互,这也是评价一个自定义View优劣的标准之一。通常需要继承View类,并重写它的onDraw()、onMeasure()等方法来实现绘制逻辑,同时通过重写onTouchEvent()等触控事件来实现交互逻辑。当然,我们还可以像实现组合控件方式那样,通过引入自定义属性,丰富自定义View的可定制型。

事件的拦截机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值