《Android群英传》读书笔记3.Android控件架构与自定义控件详解

1. Android控件架构

ViewGroup控件与View控件组成控件树,findViewById()就是在控件树中DFS查找元素。
View树结构UI界面架构图标准视图树
通过设置requestWindowFeature(Window.FEATURE_NO_TITLE)来设置全屏显示,视图树中的布局就只有Content了,这就是调用requestWindowFeature()方法一定要在调用setContentView()方法之前才能生效的原因。
当程序在onCreate()方法中调用setContentView()方法之后,ActivityManagerService会回调onResume()方法,此时系统才会把整个DecorView添加到PhoneWindow中,并让其显示出来,从而最终完成界面的绘制。

2. View的测量

onMeasure():绘制View前,对View进行测量。
MeasureSpec类(实质是32位int值):测量模式 + 测量大小
测量模式:EXACTLY(具体数值或match_parent), AT_MOST(wrap_content), UNSPECIFIED
对自定义View重写onMeasure方法,根据测量模式指定宽和高,便可在布局文件对自定义View使用android:layout_width和android:layout_height属性

3. View的绘制

onDraw(Canvas):测量好View后,通过重写onDraw()方法在Canvas上绘制所需图形
装载画布:Canvas canvas = new Canvas(bitmap);
这个bitmap用来存储所有绘制在Canvas上的像素信息,调用所有的canvas.drawXXX方法都发生在这个bitmap上,即使将bitmap装载在别的画布而非onDraw()指定的那块画布,改变了bitmap,View重绘后也会显示改变后的bitmap。

4. ViewGroup的测量

当ViewGroup的大小为wrap_content时,ViewGroup遍历子View的Measure方法来决定自己的大小。
子View测量完毕后,遍历调用子View的Layout,并指定其具体位置。
自定义ViewGroup时,通常重写ViewGroup的onLayout()方法来控制子View位置。如果需要支持wrap_content属性,必须重写onMeasure()方法。

5. ViewGroup的绘制

通常不需要绘制,除非指定背景色等会调用onDraw()方法。
使用dispatchDraw()来遍历绘制子View,并调用子View的绘制方法。

6. 自定义View

适当使用自定义View,因为Android控件已经经过打磨,用户熟悉,bug少,适配效果好

6.1. 对现有控件进行拓展

继承系统原生控件,重写onDraw()方法,在调用super.onDraw()方法前后实现自己的逻辑

6.2. 创建复合组件

通常继承一个合适的ViewGroup,再给它添加指定功能的控件

  1. 定义属性:在res/values/attrs.xml定义属性,通过TypedArray获取自定义属性集,获取完所有属性值后,需要调用TypedArray的recycle方法完成资源的回收
    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);
  2. 组合控件:动态添加控件,即addView(),并设置属性值
    给按钮设计点击事件:定义接口→暴露接口给调用者→实现接口回调
  3. 引用UI模板:需要指定引用第三方控件的名字空间
    使用系统属性:xmlns:android="http://schemas.android.com/apk/android"
    使用自定义属性:xmlns:custom="http://schemas.android.com/apk/res-auto"custom:leftText="Back",申明控件需要完整的包名
6.3. 重写View来实现全新的控件

通常继承View类,并重写onDraw()onMeasure()等方法

  • 弧线展示图:在onDraw()方法中一个个去绘制图形
  • 音频条形图:动态效果:在onDraw()方法中调用invalidate()通知View重绘,或通过postInvalidateDelayed(300)进行View的延迟重绘

7. 自定义ViewGroup

通常需要重写onMeasure()方法对子View进行测量,重写onLayout()方法确定子View的位置,重写onTouchEvent()方法增加响应事件
例子:实现ScrollView的上下滑动功能,在滑动过程中,增加一个黏性的效果,即当一个子View向上滑动大于一定的距离后,松开手指,它将自动向上滑动,显示下一个子View。同理,如果滑动距离小于一定的距离,松开手指,它将自动滑动到开始的位置。
为实现黏性效果,在onTouchEvent()方法中利用ACTION_DOWN事件、ACTION_MOVE事件和ACTION_UP事件。

8. 事件拦截机制分析

实例层级:MyViewGroupA → MyViewGroupB → MyView
事件传递顺序:MyViewGroupA → MyViewGroupB → View
事件处理顺序:View → MyViewGroupB → MyViewGroupA
事件传递(onInterceptTouchEvent)返回值:True,拦截,自己处理;False,不拦截,继续流程
事件处理(onTouchEvent)返回值:True,处理了,不用给上级;False,给上级处理
正常情况:
ViewGroupA dispatchTouchEvent
ViewGroupA onInterceptTouchEvent
ViewGroupB dispatchTouchEvent
ViewGroupB onInterceptTouchEvent
View dispatchTouchEvent
View onTouchEvent
ViewGroupB onTouchEvent
ViewGroupA onTouchEvent
事件拦截:
事件拦截
事件处理:
事件处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值