这是我参与「第四届青训营 」笔记创作活动的的第5天
一、前言
今天主要来梳理一下android中的View的渲染与交互这个过程。
- 渲染
- 布局加载
- 布局解析
- 布局渲染
- 交互
- 常用交互监听器
- 触摸事件
- 捕获触摸事件
- 事件处理流程
二、渲染
布局加载
在Activity加载布局,是很容易的,如下代码,在onCreate回调方法中通过调用setContentView方法即可。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
那么setContentView这个过程发生了什么呢?
我们可以通过查看源码来分析。
它通过AppCompatDelegate的setContentView方法,在这个方法中,找到DercorView中id为content的ViewGroup布局,通过LayoutInflater将我们的xml布局加载到此ViewGroup中。
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
布局解析
我们在布局加载中发现最后通过LayoutInflater来解析我们的布局,那么LayoutInflater做了什么工作,我们再通过源码分析。
通过分析源码,我们看到LayoutInflater.inflate方法最终调用了LayoutInflater中的rInflate方法。
然后通过分析XML布局的标签,去实例化对应的View对象,并将它添加到ViewGroup中。
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
...
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
...省略部分代码
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
...
}
布局渲染
页面绘制流程
UI渲染流程
渲染流程
三、交互
常用交互监听器
- View.OnClickListener
用于监听用户的点击操作 - View.OnLongClickListener
用于监听用户的长按操作 - View.OnFocusChangeListener
用于监听焦点变化操作 - View.OnTouchListener
用于监听用户的触摸操作
触摸事件
当用户触摸屏幕时,系统将建立一系列的MotionEvent对象,MotionEvent包含关于触摸位置的坐标、触摸的类型等信息。
基本操作类型:
- ACTION_DOWN
手指按下 - ACTION_MOVE
手指在屏幕上移动 - ACTION_UP
手指从屏幕上离开
捕获触摸事件
我们可以通过Activity和View的onTouchEvent()去捕获触摸事件。
事件处理流程
事件的处理流程主要包括以下三个方法。
- public boolean dispatchTouchEvent(MotionEvent event)
用来进行事件的分发 - public boolean onInterceptTouchEvent(MotionEvent ev)
用来进行事件的拦截 - public boolean onTouchEvent(MotionEvent event)
用来处理事件
我们可以通过一段伪代码来描述三者的关系。
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
对一个ViewGroup(布局)来说,当一个事件产生的时候,会进行分发,如果onInterceptTouchEvent返回true则代表它要进行拦截,那么这个事件就交给它本身进行处理。如果不拦截这个事件,则会继续分发给它的子控件,如此反复操作直到事件被处理。
当一个事件产生时,分发顺序为:Activity -> Window -> View。
而处理顺序为:View -> Windwo -> Activity。
也就是如果一个事件分发到最后都没有进行处理,那么这个事件会由Activity进行处理,此时Activity的回调方法onTouchEvent就会被调用。
四、总结
这里为大家简单介绍了android界面的渲染以及交互。
通过学习渲染我们发现界面上的根视图并不是我们自己创建的layout,它还有父视图。
通过了解触摸事件以及分析交互的整个过程,我们可以了解到android的事件分发机制,通过这些机制可以解决我们日常生活中的很多问题,比如交互冲突、滑动冲突。
当然这里就是简单地介绍了渲染与交互,如果有能力感兴趣的小伙伴们还可以深入去了解。
五、结语
如果喜欢或有所帮助的话,希望能点赞关注,鼓励一下作者。
如果文章有不正确或存疑的地方,欢迎评论指出。