View详解(1)

好久好久没更新了,不知道大家还有没有在看以前的一些博文,这段时间换了个坑位还是有点小忙呢!鉴于最近工作接触自定义ViewCanvas比较多,所以打算开个系列,详细讲讲View的那些事。


View是什么

要学习自定义View,我们首先应该清楚的认识到View是个什么东西。那么View究竟是什么呢?

  • 从代码层面看,View是一个类,是所有控件的直接或间接父类,详情见图-View子孙关系,图中只显示了部分View的子孙,有兴趣的朋友可以尝试自己去画下这幅图
  • 从用户角度看,View是用户界面的组成元素,View参与用户交互

那么View究竟长什么样呢?见图-View界面边界

从上图可以看出(开发者选项->打开布局边界),界面上框出了很多大小不一的矩形框,我们可以把这里的每一个框都看作一个View或一个View子孙类,在Android Studio中打开布局管理器,也能看到类似于这种效果,如图-Android Studio View界面边界。

通过上面描述,我们可以清晰的认识到,View对应着界面上的一块矩形区域,在这块矩形区域内可以进行用户交互,那么这个矩形区域内部各种各样的颜色,Icon又是怎么显示出来的呢?


View生命周期

我们都知道Android中大多数控件具有生命周期敏感性,那么View有没有生命周期呢?答案是肯定的,View作为用户交互的重要组成元素,与Activity一样具有显式生命周期,只不过一般我们不去过分强调而已。

如图-View生命周期所示,其中描述了View的整个生命周期过程,其中我们需要关注r如下几点:

  • invalidate()的同作用函数postInvalidate(),两者均用于更新View内容,只不过postInvalidate()发生在单独的线程,invalidate()发生在主线程;
  • View整个生命周期中有四个重要函数,构造器从XML文件中解析View属性,onMeasure()测量View大小,测量过程与测量模式,PaddingMargin等有关, onLayout()完成View的位置布局,onDraw()完成View内容的绘制;
  • 如上图所示,如果需要View重新计算大小,则需要调用requestLayout(),启动View的深度优先遍历过程,重新构造View树,关于View树的相关信息,我们会在后续文章中描述;
  • 上图中并没有绘制View 触屏事件相关的响应函数,会在随后的View事件处理部分进行详细描述;

View坐标系

不知道大家是否还记得我们学习绘制图形之前,最开始学习的是什么?相信大多数朋友都知道那就是坐标系,对于在View上绘制图形也是一样,首先需要要绘制图形在View内的位置,随后才能使用画笔进行绘制,那么View内部坐标系是怎样的呢?

如图-View坐标系所示,View内部的坐标原点位于View所在矩形的左上角,以屏幕水平右方向为X轴正向,以垂直向下方向为Y轴正向,那么我们经常使用的View#getTop(),View#getLeft(),View#getBottom(),View#getRight()等返回的又是哪里的距离呢?

具体的函数值说明,如图-View函数值说明所示,大家自行参考。


View事件处理

View作为参与用户交互的重要元素之一,响应用户操作必不可少,View内部的事件分发机制如图-View内部分发流程。

从图-View内部分发机制 ,在View内部事件分发过程中,事件起始于父控件调用dispatchTouchEvent,止于onTouchListener或者onTouchEvent返回true(true-事件被消耗,false-未消耗事件),如果onTouchListener返回false或为空,事件会进一步传递到onTouchEvent处理,如果onTouchEvent返回false,事件会被扔回父控件处理,如果每级都按照上述流程返回false,则事件会被上传到操作系统抛弃掉。

现有状况下我们一般有两种方式处理用户事件:

  • 重写View#onTouchEvent()
  • View onTouchListener接口

那么这两种处理方式有什么区别呢?从下面源码中我们可以看出onTouchListener的优先与onTouchEvent,所以在重写onTouchEvent无效的情况下,除了check 父控件是否向下分发事件以外,还需要check该控件是否有onTouchListener的监听。

/** Android P 源码代码片段 **/
public boolean dispatchTouchEvent(MotionEvent event) {
        ....

        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }

        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //优先响应onTouchListener
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
            //onTouchListener为空或者返回false时,响应onTouchEvent
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, );
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
        // of the gesture.
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

更多详情请关注后续更新,觉得不错的朋友记得动动手指转发哦!

本文分享自微信公众号 - 小海编码日记(gh_1f87b8c00ede),作者:小海的编码日记

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-09-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • View绘制系列(2)-View生命周期

    了解C++的小伙伴们肯定都听过构造函数和析构函数这两个名词,通过构造函数我们可以生成一个类对象,通过析构函数我们可以完成一个对象的销毁,那么对于同样面对对象的J...

    小海编码日记
  • View绘制系列(3)-自定义View简介

    经过前面两篇文章的介绍,相信大多数同学已经清楚的认识了View,那么我们来看下自定义View这个主题,在小册简介中,我们已经描述了自定义View的目的,同学们还...

    小海编码日记
  • View绘制系列(1)-View简介

    对于初级开发者而言,在面试中,经常会和面试官聊到Android四大组件,聊到Activity,其中回答最纯熟的一句话就是:

    小海编码日记
  • 教你步步为营掌握自定义 View

    国内自定义View的文章汗牛充栋,但是,即使你全部看完它们也未必能掌握这一知识点(实际上,我就几乎看完了所有的国内文章)。为什么?一言以蔽之,你是得其术不明其道...

    非著名程序员
  • View绘制系列(2)-View生命周期

    了解C++的小伙伴们肯定都听过构造函数和析构函数这两个名词,通过构造函数我们可以生成一个类对象,通过析构函数我们可以完成一个对象的销毁,那么对于同样面对对象的J...

    小海编码日记
  • SAP CDS view里association和join的区别

    Association它指明了两个View之间的关系,如果一个View A中定义了Association到另外一个View B时,View A可以把这个Asso...

    Jerry Wang
  • Android:你要了解的自定义View基础概念都在这里了!

    自定义View原理是Android开发者必须了解的基础,在了解自定义View之前,你需要有一定的知识储备。

    Android技术干货分享
  • SAP CDS view里,什么时候用left join,什么时候用association

    版权声明:本文为博主汪子熙原创文章,未经博主允许不得转载。 https://jerry.bl...

    Jerry Wang
  • 自定义View基础 - 最易懂的自定义View原理系列(1)

    对于多View的视图,结构是树形结构:最顶层是ViewGroup,ViewGroup下可能有多个ViewGroup或View,如下图:

    Carson.Ho
  • View绘制系列(3)-自定义View简介

    经过前面两篇文章的介绍,相信大多数同学已经清楚的认识了View,那么我们来看下自定义View这个主题,在小册简介中,我们已经描述了自定义View的目的,同学们还...

    小海编码日记

扫码关注云+社区

领取腾讯云代金券