不诗意的女程序猿不是好厨师,大家好,我是李诗雨~
序:最近在工作中使用到了各种自定义控件,也更深刻的理解了自定义控件的重要性,所以就建了一个专栏来专门整理自定义控件的相关知识。我打算先从理论知识说起,然后再把项目中使用的自定义控件整理后写为博客发表,并且源码也会一并上传。理论知识部分,个人觉得整理的还是很详细的而且重点分明,无论是对面试还是对代码的理解都起到了很好的辅助作用。
View相关知识的理解
在说自定义控件之前让我先来了解一下View及ViewGroup
整个View树的结构图:
关于View和ViewGroup我们需要注意以下几点:
1. 手机屏幕上的整个界面只有一个根View
如何得到它:activity.getWindow().getDecorview() —>PhoneWindow$decorView
本质类型: FrameLayout
注意: setContentView():执行添加的视图不是整个界面的根View
2.一个View只会有一个父View(ViewGroup),一个ViewGroup可以有多个子View
a.得到父视图: view.getParent(),可以将返回的ViewParent强转为指定的ViewGroup
b.不是所有的View都能添加子view,只有ViewGroup 及其子类才能添加
View是什么
- View类是所有用来构建用户界面的组件的基类
- 一个View对象占用屏幕上的一个矩形区域, 它负责界面的绘制和事件处理
- 手机屏幕上所有看得见摸得着的都是View
- 常见的View:TextView,EditText,Button,ImageView,ProgressBar…
ViewGroup是什么
- ViewGroup类是View的一个子类, 是各种布局(五大布局)的基类
- 一个ViewGroup可以包含多个子View(ViewGroup)
- 作用: 控制子View的布局,view.layout(left, top, right, bottom)
- ViewManager及相关方法:
① addView():添加子View
② removeView():删除子View
③ updateViewLayout():更新子View - 常见的ViewGroup:LinearLayout,RelativeLayout,FrameLayout,ListView…
View(及其子类)的生命周期
1. 创建对象
- 创建方式(2种):
new MyView(context)
加载布局文件,即自定义View必须使用全类名标签 流程方法
①构造器
Xxx(Context context)
Xxx(Context context, AttributeSet set)
②onFinishInflate()
只有布局的方式才会调用
重写的目的: 得到子View –>getChildAt(int index):index按照加载顺序排列
onAttachedToWindow()–>重写: 得到子View补充 Activity的onResume()执行之后才会进入后面的流程
2. 测量
作用:计算并确定视图的大小(width/height)
流程方法:
①.mesure() :系统在此方法中测量计算出当前视图的宽高,此方法不能重写
②.onMesure():
当mearure()中 计算出的视图的宽高就会调用此方法, 在此方法默认保存的
视图测量的宽高
注意:视图测量的宽高不等同于视图的宽高。获取的时机不同
重写的意义:得到当前视图/子视图测量的宽高;保存我们自己指定的宽高
3. 布局
作用:确定视图显示的坐标(left, top, right, bottom)
流程方法:
①.layout() :layout(l, t, r, b),不会重写此方法, 只会调用视图对象的此方法, 指
定其新的显示位置
②.onLayout()
在layout()的过程中 如果①视图的位置change或②强制重新布局就会调
此方法
重写它: 可以对子View进行重新布局,调用childView.layout(left, top, right,
bottom)强制重新布局: view.requestLayout()
4.绘制
作用:画出视图的样子
流程方法:
draw(),绘制视图通用的部分,确定绘制的流程,一般不会重写此方法
onDraw(),重写此方法,绘制自己需要的样子,一些具体的View类(如:TextView,ImageView)都重写了此方法强制重绘:
invalidate():只能在主线程执行
postInvalidate():可以在主线程或分线程执行
5.事件处理
流程方法:
dispatchTouchEvent():
分发事件,从外向里一层一层分发, 分发到事件发生的最里面的视图对象
boolean onInterceptTouchEvent():
拦截请求, 只有return true才拦截成功,如果事件被拦截,事件不会再向内层分
发, 交给当前的视图处理
boolean onTouchEvent():
处理事件:消费事件的条件: return true
requestDisallowInterceptTouchEvent(true)
反拦截–>view.getParent().requestDisallowInterceptTouchEvent(true)事件机制:
①分发: 将TouchEvent对象从Activity对象开始, 由外向内分发给对应的布局和子View对象(由外向内分发)。②处理: 回调OnTouchListener的boolean onTouch()
回调View的boolean onTouchEvent()③消费: 回调方法返回true
④拦截: onInterceptTouchEvent()执行返回true
如果返回true, TouchEvent就不会再传入子View对象⑤反拦截: view.getParent().requestDisallowInterceptTouchEvent(true)
使父View不能再拦截, 事件就会分发到当前View对象
拦截与反拦截,都是在分发的时候就要决定的。
6.死亡
什么时候死亡:
Activity死亡之前
视图对象被移除流程方法
onDetachedFromWindow()