(面试)View相关知识

1、View绘制流程

  • onMeasure() 确定View的测量宽高。
  • onLayout() 确定View的最终宽高四个顶点的位置。
  • onDraw() 将View 绘制到屏幕上。

2、MeasureSpec有三种测量模式:

2.1. EXACTLY(精确模式)

  • 含义:父容器明确指定了子View的精确尺寸,子View必须使用该尺寸
  • 典型场景:
    • 布局中设置了固定值(如android:layout_width="100dp")。
    • View的宽/高设置为match_parent,且父容器有确定尺寸。
  • View行为:必须直接使用MeasureSpec中的size作为最终尺寸。

2.2. AT_MOST(最大模式)

  • 含义:父容器指定了子View的最大可用尺寸,子View的尺寸不能超过该值,但可以更小

  • 典型场景:

    • View的宽/高设置为wrap_content

    • 父容器为ScrollViewRecyclerView等可滚动的容器。

  • View行为:根据自身内容计算尺寸,但最终尺寸不能超过MeasureSpec中的size


2.3. UNSPECIFIED(未指定模式)

  • 含义:父容器对子View无任何约束,子View可以自由决定尺寸(通常根据自身逻辑或内容)
  • 典型场景:
    • 自定义ViewViewGroup需要多次测量(如ListView测量子View的高度)。
    • 系统内部测量(如ScrollView在测量子View的滚动范围时)。
  • View行为:完全由自身决定尺寸(可能使用默认值或内容所需尺寸)。

3、事件分发机制

3.1 事件分发:dispatchTouchEvent

用来进行事件的分发,如果事件能够传递给当前View,则该方法一定会被调用。返回结果受当前View的onTouchEvent和下级的dispatchTouchEvent的影响,表示是否消耗当前事件。

原型:public boolean dispatchTouchEvent(MotionEvent ev)

return:

  • ture:当前View消耗所有事件
  • false:停止分发,交由上层控件的onTouchEvent方法进行消费,如果本层控件是Activity,则事件将被系统消费,处理

3.2 事件拦截:onInterceptTouchEvent

需注意的是在Activity,ViewGroup,View中只有ViewGroup有这个方法。故一旦有点击事件传递给View,则View的onTouchEvent方法就会被调用

在dispatchTouchEvent内部使用,用来判断是否拦截事件。如果当前View拦截了某个事件,那么该事件序列的其它方法也由当前View处理,故该方法不会被再次调用,因为已经无须询问它是否要拦截该事件。

原型:public boolean onInterceptTouchEvent(MotionEvent ev)

return:

  • ture:对事件拦截,交给本层的onTouchEvent进行处理
  • false:不拦截,分发到子View,由子View的dispatchTouchEvent进行处理
  • super.onInterceptTouchEvent(ev):默认不拦截

3.3 事件处理:onTouchEvent

在dispatchTouchEvent中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一事件序列中,当前View无法再接受到剩下的事件,并且事件将重新交给它的父元素处理,即父元素的onTouchEvent会被调用

原型:public boolean onTouchEvent(MotionEvent ev)

return:

  • true:表示onTouchEvent处理后消耗了当前事件
  • false:不响应事件,不断的传递给上层的onTouchEvent方法处理,直到某个View的onTouchEvent返回true,则认为该事件被消费,如果到最顶层View还是返回false,则该事件不消费,将交由Activity的onTouchEvent处理。
  • super.onTouchEvent(ev):默认消耗当前事件,与返回true一致。

事件分发机制总结:

在分析事件分发机制时,应该从事件分发的顺序入手一步一步解剖。从上文我们知道事件分发顺序为:Activity->Window->DecorView->ViewGroup->View。由于Window与DecorView可以看作是Activity->ViewGroup的过程,故这里将从三部分通过源码来分析事件分发机制:

  1. Activity对点击事件的分发机制
  2. ViewGroup对点击事件的分发机制
  3. View对点击事件的分发机制
  • 当一个点击事件发生后,总是先传递给当前的Activity,由Activity的dispatchTouchEvent进行分发,而Activity会将事件传递给Window,然后由Window的唯一实现类PhoneWindow将事件传递给DecorView,接着DecorView将事件传递给自己的父类ViewGroup,此时的ViewGroup就是通过setContentView所设置的View,故可以称为顶级View,这时候ViewGroup可能是自己处理该事件或者传递给子View,但是最终都会调用View的dispatchTouchEvent来处理事件。
  • 在View的dispatchTouchEvent中,如果设置了onTouchListener,会调用其onTouch方法,如果onTouch返回true,则不再调用onTouchEvent。如果有设置点击事件,则在onTouchEvent会调用onClick方法。如果子View的onTouchEvent返回了false,则表示不消耗事件,事件会回传给上一级的ViewGroup的onTouchEvent,如果所有的ViewGroup都没有返回true,则最终会回传到Activity的onTouchEvent。

4、requestLayout(), invalidate(), postInvalidate() 方法区别

requestLayout方法只会导致当前view的measure和layout,而draw不一定被执行,只有当view的位置发生改变才会执行draw方法,因此如果要使当前view重绘需要调用invalidate。

invalidate在UI线程中调用,postInvalidate在非UI线程中调用。因为android的UI线程是非线程安全的,所以在非UI线程中,需要使用postInvalidate来使View重绘。view调用invalidate将导致当前view的重绘(draw调用),view的父类将不会执行draw方法viewGroup调用invalidate会使viewGroup的子view调用draw,也就是viewGroup内部的子view进行重绘

5、滑动冲突如何解决

举例(直播间左下角文字消息列表):ViewPager2嵌套垂直滑动的Fragment,Fragment布局中有一个RecyclerView消息列表。RececlerView上下滑动会与ViewPager2产生冲突。
解决办法:自定义一个继承RecyclerView的类,在dispatchTouchEvent方法中调用getParent().requestDisallowInterceptTouchEvent(true)   (true父控件不会拦截事件)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值