Android&UI编程(二) | 青训营笔记

98 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的的第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);
        }
        ...
    }

布局渲染

页面绘制流程

image.png

UI渲染流程

image.png

渲染流程

image.png

三、交互

常用交互监听器

  1. View.OnClickListener
    用于监听用户的点击操作
  2. View.OnLongClickListener
    用于监听用户的长按操作
  3. View.OnFocusChangeListener
    用于监听焦点变化操作
  4. 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就会被调用。

image.png

四、总结

这里为大家简单介绍了android界面的渲染以及交互。
通过学习渲染我们发现界面上的根视图并不是我们自己创建的layout,它还有父视图。
通过了解触摸事件以及分析交互的整个过程,我们可以了解到android的事件分发机制,通过这些机制可以解决我们日常生活中的很多问题,比如交互冲突、滑动冲突
当然这里就是简单地介绍了渲染与交互,如果有能力感兴趣的小伙伴们还可以深入去了解。

五、结语

如果喜欢或有所帮助的话,希望能点赞关注,鼓励一下作者。
如果文章有不正确或存疑的地方,欢迎评论指出。