Android安卓进阶技巧之自定义View系列——MeasureSpec

Posted 学习Android的第1024天

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android安卓进阶技巧之自定义View系列——MeasureSpec相关的知识,希望对你有一定的参考价值。

【Android】自定义 View 系列- 绘制流程 一文中,在测量过程中,是通过 一定的规则 得出最后我们测量的宽高,然后通过 setMeasuredDimension() 保存结果。

那么这里说到的规则主要就是—MeasureSpec 和 LayoutParams

MeasureSpec 是??

//源码描述
/**
* A MeasureSpec encapsulates the layout requirements passed from parent to child.
* Each MeasureSpec represents a requirement for either the width or the height. 
* A MeasureSpec is comprised of a size and a mode. There are three possible modes:
* UNSPECIFIED
* The parent has not imposed any constraint on the child. It can be whatever size it wants.
* EXACTLY
* The parent has determined an exact size for the child. The child is going to be given  
* those bounds regardless of how big it wants to be.
* AT_MOST
* The child can be as large as it wants up to the specified size.
*/ 

提取主要内容:
1.MeasureSpec 父 View 对子 View 的宽高要求。
2.MeasureSpec 包含了两个信息 模式(mode)和大小(size)
3.一共有三种模式:
UNSPECIFIED- 任意大小(It can be whatever size it wants )-例如 recyclerview 子View 的大小是超过父View 的大小的
EXACTLY - 精确值(regardless of how big is wants to be, an exact size )、
AT_MOST - 在父View 的限制范围中任意大小(as large as it wants up to the specified size).

但是,对于我们开发者来说,我们知道一个 View 的大小,不是应该由我们通过 layout_width 和 layout_height 决定的吗?

对,也不对。事实是,在对于任意一个View (包括ViewGroup),最终的大小是由父 View 的要求(MeasureSpec)和子View 自己的需求(LayoutParams-layout_width & layout_height)决定的。具体是如何决定的呢?

/**
* @param spec The requirements for this view
* @param padding The padding of this view for the current dimension and
*        margins, if applicable
* @param childDimension How big the child wants to be in the current
*        dimension
* @return a MeasureSpec integer for the child
*/
public static int getChildMeasureSpec(int spec, int padding, int childDimension) 
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);

    int size = Math.max(0, specSize - padding);

    int resultSize = 0;
    int resultMode = 0;

    switch (specMode) 
    // Parent has imposed an exact size on us
    case MeasureSpec.EXACTLY:
        if (childDimension >= 0) 
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
         else if (childDimension == LayoutParams.MATCH_PARENT) 
            // Child wants to be our size. So be it.
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
         else if (childDimension == LayoutParams.WRAP_CONTENT) 
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        
        break;

    // Parent has imposed a maximum size on us
    case MeasureSpec.AT_MOST:
        if (childDimension >= 0) 
            // Child wants a specific size... so be it
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
         else if (childDimension == LayoutParams.MATCH_PARENT) 
            // Child wants to be our size, but our size is not fixed.
            // Constrain child to not be bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
         else if (childDimension == LayoutParams.WRAP_CONTENT) 
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        
        break;

    // Parent asked to see how big we want to be
    case MeasureSpec.UNSPECIFIED:
        if (childDimension >= 0) 
            // Child wants a specific size... let them have it
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
         else if (childDimension == LayoutParams.MATCH_PARENT) 
            // Child wants to be our size... find out how big it should
            // be
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
         else if (childDimension == LayoutParams.WRAP_CONTENT) 
            // Child wants to determine its own size.... find out how
            // big it should be
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        
        break;
    
    //noinspection ResourceType
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
 

(上面代码逻辑很简单,源码的注解也很贴心了,请各位耐心读完。然后再看下面的总结表格,切忌死记硬背下面的表格)

总结为表格,其中:childDimension(子View 自身大小)、size(父view剩余空间)

EXACTLYAT_MOSTUNSPECIFIED
具体值(即 childDimension ≥ 0)模式:EXACTLY大小:childDimension模式:EXACTLY大小:childDimension模式:EXACTLY大小:childDimension
match_parent模式:EXACTLY大小:size模式:AT_MOST大小:size模式:UNSPECIFIED大小:View.sUseZeroUnspecifiedMeasureSpec ? 0 : size
wrap_content模式:AT_MOST大小:size模式:AT_MOST大小:size模式:UNSPECIFIED大小:View.sUseZeroUnspecifiedMeasureSpec ? 0 : size

今日疑问:为什么在一次事件序列中 onInterceptTouchEvent() 返回 true 后就不会再次执行onInterceptTouchEvent() 了,而在下一次事件序列中又会再次触发吗?

这个问题,我暂时没有找到切确的答案。截止目前研究,暂时认为是 mGroupFlags 标志被改变后,导致进入 onInterceptTouchEvent() 不再触发,并且在 UP/CANCEL/HOVER_MOVE 和下次事件序列开始(即 DOWN) 会调用 resetTouchState() 重置状态。这样又可以触onInterceptTouchEvent()。

对于 MeasureSpec 的内容和 上述疑问欢迎大家评论区交流~

NCEL/HOVER_MOVE 和下次事件序列开始(即 DOWN) 会调用 resetTouchState() 重置状态。这样又可以触onInterceptTouchEvent()。

对于 MeasureSpec 的内容和 上述疑问欢迎大家评论区交流~

最后

有小伙伴私信问Compose的问题,好不好用啊,现在要不要学啊?

其实答案很简单,自从谷歌2019年公布了声明式UI框架Jetpack Compose后,两年多的时间,各种大力宣传,和大量资源的倾斜,API功能都趋于稳定了。

至于好不好用,各种用过的同行都是持肯定态度的。优势大概就是这四点:

强大的工具和直观的Kotlin API
简化并加速了Android上的UI开发
可以帮助开发者用更少更直观的代码创建View
有更强大的功能,以及还能提高开发速度

这么大的优势,毋庸置疑,肯定是要学的嘛,而且越快掌握越好。别等刀架到脖子上了,才去练金钟罩。

至于怎么快速上手,可以给大家免费分享一份**《Jetpack Compose 完全开发手册》**,手把手教大家从入门到精通。

由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

有需要的话可以点下面二维码免费领取↓↓↓

第一章 初识 Jetpack Compose

  • 为什么我们需要一个新的UI 工具?

  • Jetpack Compose的着重点

    加速开发
    强大的UI工具
    直观的Kotlin API

  • API 设计

  • Compose API 的原则
    一切都是函数
    顶层函数(Top-level function)
    组合优于继承
    信任单一来源

  • 深入了解Compose
    Core
    Foundation
    Material

  • 插槽API

第二章 Jetpack Compose构建android UI

  • Android Jetpack Compose 最全上手指南
    Jetpack Compose 环境准备和Hello World
    布局
    使用Material design 设计
    Compose 布局实时预览
    ……

  • 深入详解 Jetpack Compose | 优化 UI 构建
    Compose 所解决的问题
    Composable 函数剖析
    声明式 UI
    组合 vs 继承
    封装
    重组
    ……

  • 深入详解 Jetpack Compose | 实现原理
    @Composable 注解意味着什么?
    执行模式
    Positional Memoization (位置记忆化)
    存储参数
    重组
    ……

第三章 Jetpack Compose 项目实战演练(附Demo)

  • Jetpack Compose应用1
    开始前的准备
    创建DEMO
    遇到的问题

  • Jetpack Compose应用2
  • Jetpack Compose应用做一个倒计时器
    数据结构
    倒计时功能
    状态模式
    Compose 布局
    绘制时钟

  • 用Jetpack Compose写一个玩安卓App
    准备工作
    引入依赖
    新建 Activity
    创建 Compose
    PlayTheme
    画页面
    底部导航栏
    管理状态
    添加页面

  • 用Compose Android 写一个天气应用
    开篇
    画页面
    画背景
    画内容
    ……

  • 用Compose快速打造一个“电影App”
    成品
    实现方案
    实战
    不足
    ……

由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
有需要的话可以点下面二维码免费领取↓↓↓

以上是关于Android安卓进阶技巧之自定义View系列——MeasureSpec的主要内容,如果未能解决你的问题,请参考以下文章

Android进阶之自定义View实战九宫格手势解锁实现

Android进阶之自定义View实战九宫格手势解锁实现

Android进阶之自定义View实战九宫格手势解锁实现

Android进阶之自定义View实战贝塞尔曲线应用

Android进阶之自定义View实战贝塞尔曲线应用

android进阶之自定义view(文字圆形边框)