NumberPicker分析(二)

news2025/1/15 7:34:13

NumberPicker分析(二)

NumberPicker继承自LinearLayout。一般而言,无论是继承自View,还是继承自ViewGroup,必然会经过如下的几个阶段:

  1. onMeasure
  2. onLayout
  3. onDraw

onMeasure

onMeasure方法测量当前控件大小,为正式布局提供建议。测量完成以后,要通过 setMeasuredDimen ion(int,int)函数设置给系统
NumberPicker中的构造方法中,通过调试断点可知(结合上一节中的Widget.Holo.NumberPickerstyle也可知),初始化时:

  • mMinHeight没有设置值(默认为-1, 表示SIZE_UNSPECIFIED
  • mMaxHeight有设置值(在本人测试机器上为495
  • mMinWidth有设置值(在本人测试机器上为176
  • mMaxWidth没有设置值(默认为-1, 表示SIZE_UNSPECIFIED

之后在tryComputeMaxWidth()方法中,给mMaxWidth又设置了值,mMaxWidth = mMinWidth
此时mMaxWidthmMaxHeight就都有值了

NumberPickeronMeasure方法如下:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (!mHasSelectorWheel) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }
        // Try greedily to fit the max width and height.
        /**
        * 1.widthMeasureSpec是父View传递给当前View的一个建议值
        * 2.mMaxWidth = 176
        */
        final int newWidthMeasureSpec = makeMeasureSpec(widthMeasureSpec, mMaxWidth);
        /**
        * 1.heightMeasureSpec是父View传递给当前View的一个建议值
        * 2.mMaxHeight = 495
        */
        final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, mMaxHeight);
        super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
        // Flag if we are measured with width or height less than the respective min.
        // 在这里还需要考虑最小值限制的问题
        /**
        * 1.mMinWidth = 176, getMeasuredWidth() = 176
        * 2.按resolveSizeAndStateRespectingMinSize方法,需要创建一个新的widthMeasureSpec
        */
        final int widthSize = resolveSizeAndStateRespectingMinSize(mMinWidth, getMeasuredWidth(),
                widthMeasureSpec);
        /**
        * 1.mMinHeight = -1, getMeasuredWidth() = 495
        * 2.按resolveSizeAndStateRespectingMinSize方法,直接返回heightMeasureSpec
        */        
        final int heightSize = resolveSizeAndStateRespectingMinSize(mMinHeight, getMeasuredHeight(),
                heightMeasureSpec);
        setMeasuredDimension(widthSize, heightSize);
    }

makeMeasureSpec方法和resolveSizeAndStateRespectingMinSize方法,定义如下:

    /**
     * Makes a measure spec that tries greedily to use the max value.
     *
     * @param measureSpec The measure spec.
     * @param maxSize The max value for the size.
     * @return A measure spec greedily imposing the max size.
     */
    private int makeMeasureSpec(int measureSpec, int maxSize) {
        if (maxSize == SIZE_UNSPECIFIED) {
            return measureSpec;
        }
        final int size = MeasureSpec.getSize(measureSpec);
        final int mode = MeasureSpec.getMode(measureSpec);
        switch (mode) {
            case MeasureSpec.EXACTLY:
                return measureSpec;
            case MeasureSpec.AT_MOST:
                return MeasureSpec.makeMeasureSpec(Math.min(size, maxSize), MeasureSpec.EXACTLY);
            case MeasureSpec.UNSPECIFIED:
                return MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.EXACTLY);
            default:
                throw new IllegalArgumentException("Unknown measure mode: " + mode);
        }
    }

    /**
     * Utility to reconcile a desired size and state, with constraints imposed
     * by a MeasureSpec. Tries to respect the min size, unless a different size
     * is imposed by the constraints.
     *
     * @param minSize The minimal desired size.
     * @param measuredSize The currently measured size.
     * @param measureSpec The current measure spec.
     * @return The resolved size and state.
     */
    private int resolveSizeAndStateRespectingMinSize(
            int minSize, int measuredSize, int measureSpec) {
        if (minSize != SIZE_UNSPECIFIED) {
            final int desiredWidth = Math.max(minSize, measuredSize);
            return resolveSizeAndState(desiredWidth, measureSpec, 0);
        } else {
            return measuredSize;
        }
    }

在layout布局中,设置layout_width="wrap_content" , layout_height="wrap_content",相当于是 MeasureSpec.AT_MOST,所以最终调用的都是如下的方法:

MeasureSpec.makeMeasureSpec(Math.min(size, maxSize), MeasureSpec.EXACTLY)

width
height

onLayout

onLayout布局所有的子控件
NumberPickeronLayout方法主要用来:

  • 居中布局EditTextmInputText
  • 初始化选择轮 - initializeSelectorWheel
  • 初始换边缘Fading效果 - initializeFadingEdges
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if (!mHasSelectorWheel) {
            super.onLayout(changed, left, top, right, bottom);
            return;
        }
        final int msrdWdth = getMeasuredWidth();
        final int msrdHght = getMeasuredHeight();

        // Input text centered horizontally.
        final int inptTxtMsrdWdth = mInputText.getMeasuredWidth();
        final int inptTxtMsrdHght = mInputText.getMeasuredHeight();
        final int inptTxtLeft = (msrdWdth - inptTxtMsrdWdth) / 2;
        final int inptTxtTop = (msrdHght - inptTxtMsrdHght) / 2;
        final int inptTxtRight = inptTxtLeft + inptTxtMsrdWdth;
        final int inptTxtBottom = inptTxtTop + inptTxtMsrdHght;
        mInputText.layout(inptTxtLeft, inptTxtTop, inptTxtRight, inptTxtBottom);

        if (changed) {
            // need to do all this when we know our size
            initializeSelectorWheel();
            initializeFadingEdges();
            //分割线位置
            mTopSelectionDividerTop = (getHeight() - mSelectionDividersDistance) / 2
                    - mSelectionDividerHeight;
            mBottomSelectionDividerBottom = mTopSelectionDividerTop + 2 * mSelectionDividerHeight
                    + mSelectionDividersDistance;
        }
    }

initializeSelectorWheel方法

initializeSelectorWheel方法中,计算和初始化了许多值
initializeSelectorWheel
其中的关系,可以表示如下:
关系
并且在这里设置了mCurrentScrollOffset = mInitialScrollOffset;

onDraw

NumberPicker的构造方法中有如下的设置:

        // By default Linearlayout that we extend is not drawn. This is
        // its draw() method is not called but dispatchDraw() is called
        // directly (see ViewGroup.drawChild()). However, this class uses
        // the fading edge effect implemented by View and we need our
        // draw() method to be called. Therefore, we declare we will draw.
        setWillNotDraw(!mHasSelectorWheel);

从前面的文章可知,mHasSelectorWheelture,所以这里相当于是setWillNotDraw(false)

setWillNotDraw

public void setWillNotDraw (boolean willNotDraw)

If this view doesn’t do any drawing on its own, set this flag to allow further optimizations. By default, this flag is not set on View, but could be set on some View subclasses such as ViewGroup. Typically, if you override onDraw(android.graphics.Canvas) you should clear this flag.
如果这个视图自己不做任何绘图,设置这个标志以允许进一步优化。 默认情况下,此标志未在 View 上设置,但可以在某些 View 子类(例如 ViewGroup)上设置。 通常,如果您覆盖 onDraw(android.graphics.Canvas) 您应该清除此标志。

设置setWillNotDraw(false)后,ViewGrouponDraw方法就可以被调用了

具体的onDraw方法如下:

    @Override
    protected void onDraw(Canvas canvas) {
      	....
        float x = (mRight - mLeft) / 2;
        float y = mCurrentScrollOffset;

        // draw the virtual buttons pressed state if needed
        ....

        // draw the selector wheel
        int[] selectorIndices = mSelectorIndices;
        for (int i = 0; i < selectorIndices.length; i++) {
            int selectorIndex = selectorIndices[i];
            String scrollSelectorValue = mSelectorIndexToStringCache.get(selectorIndex);
            // Do not draw the middle item if input is visible since the input
            // is shown only if the wheel is static and it covers the middle
            // item. Otherwise, if the user starts editing the text via the
            // IME he may see a dimmed version of the old value intermixed
            // with the new one.
            if ((showSelectorWheel && i != SELECTOR_MIDDLE_ITEM_INDEX) ||
                (i == SELECTOR_MIDDLE_ITEM_INDEX && mInputText.getVisibility() != VISIBLE)) {
                //绘制文字
                canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint);
            }
            y += mSelectorElementHeight;
        }

        // draw the selection dividers 
        // 绘制分割线
        if (showSelectorWheel && mSelectionDivider != null) {
            // draw the top divider
            int topOfTopDivider = mTopSelectionDividerTop;
            int bottomOfTopDivider = topOfTopDivider + mSelectionDividerHeight;
            mSelectionDivider.setBounds(0, topOfTopDivider, mRight, bottomOfTopDivider);
            mSelectionDivider.draw(canvas);

            // draw the bottom divider
            int bottomOfBottomDivider = mBottomSelectionDividerBottom;
            int topOfBottomDivider = bottomOfBottomDivider - mSelectionDividerHeight;
            mSelectionDivider.setBounds(0, topOfBottomDivider, mRight, bottomOfBottomDivider);
            mSelectionDivider.draw(canvas);
        }
    }

概括来说就是遍历mSelectorIndices(第一次值为[4, 0, 1]),绘制text,绘制分割线。注意这里的y

float y = mCurrentScrollOffset; //初始值
...
y += mSelectorElementHeight; //每次循环后

结合上面的示意图,即可明白其中的含义

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/443981.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Faster RCNN系列2——RPN的真值与预测值概述

Faster RCNN系列&#xff1a; Faster RCNN系列1——Anchor生成过程 Faster RCNN系列2——RPN的真值与预测值概述 Faster RCNN系列3——RPN的真值详解与损失值计算 Faster RCNN系列4——生成Proposal与RoI Faster RCNN系列5——RoI Pooling与全连接层 对于目标检测任务&#xf…

Replicator简介

Replicator 文章目录 ReplicatorReplicator简介合成数据训练背后的理论Replicator核心组件已知的问题 Replicator简介 Omniverse Replicator 是一个高度可扩展的框架&#xff0c;构建在可扩展的 Omniverse 平台上&#xff0c;可生成物理上准确的 3D 合成数据&#xff0c;以加速…

传输线的物理基础(十):特性阻抗的频率变化

到目前为止&#xff0c;我们一直假设传输线的特性阻抗随频率保持不变。正如我们所见&#xff0c;从传输线前端看&#xff0c;输入阻抗与频率密切相关。毕竟&#xff0c;在低频时&#xff0c;远端开路的传输线的输入阻抗看起来像一个电容器&#xff0c;阻抗开始很高&#xff0c;…

JavaScript中的执行上下文和执行栈

执行上下文概念以及理解 执行上下文是评估和执行JavaScript代码环境的抽象概念&#xff0c;但我们在JavaScript中所做的声明变量&#xff0c;声明函数&#xff0c;执行函数。他们都是在执行上下文中运行&#xff0c;也有了所谓的作用域。 执行上下文的类型 执行上下文分为三…

创建vite+vue+electron项目

写在前面的废话 首先&#xff0c;这是一篇缝合文&#xff0c;我的目的就是想用vite、vue结合electron打包一个windows应用&#xff1b;其次&#xff0c;项目只是这三个工具的简单应用&#xff0c;目前还没有往里面添加其他内容。再次&#xff0c;项目过程中参考了google的多篇文…

执行数学的运算

数学是计算机编程的重要能力。遗憾的是&#xff0c;对shell脚本来说&#xff0c;这个处理过程比较麻烦。在shell脚本中两种途径来进行数学运算。 expr命令 最开始&#xff0c;Bourne shell提供了一个特别的命令用来处理数学表达式。expr命令允许在命令行上处理数学数学表达式。…

EMQX vs VerneMQ | 2023 MQTT Broker 对比

引言 EMQX 和 VerneMQ 都是用 Erlang/OTP 开发的高性能、分布式开源 MQTT Broker&#xff0c;以其稳定性、容错性和扩展性著称。 EMQX 是目前全球最受欢迎的 MQTT Broker 之一&#xff0c;而 VerneMQ 项目近年来却没有积极地开发和维护。 本文是《2023 年 MQTT Broker 对比》…

可视化电子标签在仓储管理上的应用

随着经济快速增长和激烈的国内外市场竞争&#xff0c;要求企业运作的每个环节反应要迅速&#xff0c;经济转型要求更趋向改善工作流程、提高作业效率、减低运作成本、增加企业效益成为当务之急。虽然许多企业都有实施库存管理系统&#xff0c;但系统主要以人工盘点和走单据流程…

请收下这些软件测试学习干货,不看后悔一辈子

学习软件测试的过程中会遇到很多很多的困难和挑战&#xff0c;只有跨过这些困难和挑战&#xff0c;才有机会挑战软件测试领域的高薪offer。今天我们就来梳理一下&#xff0c;学习软件测试的过程中&#xff0c;我们一般都会遇到哪些困难&#xff0c;我们又当如何克服这些困难。 …

ChatGPT/大模型+零代码,给中小企业带来哪些机会?

ChatGPT让2023年成了AI之年。正如iPhone在2007年开启了智能手机时代&#xff0c;我们现在正在进入人工智能时代。 新形势下&#xff0c;零代码应如何借势发力&#xff1f;伙伴云“AI零代码”给出了答案。 作为零代码领域的头部平台&#xff0c;伙伴云全量发布【AI零代码应用搭…

暖通空调系统智能化故障检测诊断研究综述与展望

暖通空调系统智能化故障检测诊断研究综述与展望 【摘 要】暖通空调系统智能化化故障检测与诊断对提高运维水平和能源效率具有重要意义。 本文总结了暖通空调故障检测与诊断领域近二十多年来的研究历程&#xff0c;探讨了基于规则、基于模型和基于数据等三类主流方法的优劣&…

一键生成!如何为整个go项目自动添加单测

效果 为go项目中每个go文件生成对应的test文件&#xff0c;为每个接口生成对应的单测接口。 类似于这样&#xff0c;为go项目中每个包都生成一个test文件&#xff0c;单测模板如下&#xff1a; 比如函数接口为func releaseEndpoint(instanceID string, endpointID string) er…

微信小程序中使用 wx.getLocation获取当前详细位置并计算距离

文章目录 前言1&#xff0c;wx.getLocation()2&#xff0c;获取详细地址3&#xff0c;计算距离4&#xff0c;报错信息&#xff1a; getLocation:fail 频繁调用会增加电量损耗5&#xff0c;报错信息&#xff1a; 请求源未被授权 前言 wx.getLocation只能够获取经纬度&#xff0c…

java Maven 的理解

一、maven项目产生的原因 当开发两个Java项目&#xff0c;姑且把它们称为A、B&#xff0c;其中A项目中的一些功能依赖于B项目中的某些类&#xff0c;那么如何维系这种依赖关系的呢&#xff1f; 答&#xff1a;可以将B项目打成jar包&#xff0c;然后在A项目的Library下导入B的ja…

保姆级教程|昨晚撸了一个ChatGPT群聊机器人

前言 近期ChatGPT可以说是太火了&#xff0c;问答、写论文、写诗、写代码&#xff0c;只要输入精确的prompt&#xff0c;他的表现总是让人惊喜。本着打不过就加入的原则。要是把ChatGPT拉入群聊中&#xff0c;会是怎样一番场景&#xff1f;说做就做&#xff0c;花了1个晚上捣鼓…

ChatGPT与文心一言对比思考

ChatGPT与文心一言对比思考 1. 目前在国内比较广泛被认知的ai模型有什么 我目前通过各种渠道注册到的账号有3个,按照了解到然后注册的顺序分别是 ChatGPTnewbing文心一言 3种ai的注册渠道 ChatGPT注册: 科学上网注册寻找外网手机号发送短信 newbing注册: 科学上网注册微软账…

政企数智办公巡展回顾 | 通信赋能传统行业数智化转型的应用实践

在宏观政策引导、技术革新与企业内部数字化改革需求的共同驱使下&#xff0c;数智办公已经成为各行各业转型升级的必由之路。关注【融云 RongCloud】&#xff0c;了解协同办公平台更多干货。 近期&#xff0c;“连接无界 智赋未来” 融云 2023 政企数智办公巡展在北京、杭州相…

【Java】EnumSet的使用

一、什么是EnumSet? EnumSet是用于枚举类的专用Set集合。 它实现了Set接口并且继承AbstractSet。 当计划使用EnumSet时,必须考虑以下几点: 1、它只能包含枚举值,并且所有值必须属于同一个枚举。 2、它不允许添加 null,在尝试这样做时会抛出NullPointerException。 3、它不…

【SpringCloud AlibabaSentinel实现熔断与限流】

本笔记内容为尚硅谷SpringCloud AlibabaSentinel部分 目录 一、Sentinel 1、官网 2、Sentinel是什么 3、下载 4、特性 5、使用 二、安装Sentinel控制台 1、sentinel组件由2部分构成 2、安装步骤 1.下载 2.运行命令 3.访问sentinel管理界面 三、初始化演示工程 …

KVM虚拟机的磁盘无损扩容方法-qcow2格式的

起因&#xff1a;我的KVM主机上安装了基于Debian11的 虚拟机母鸡&#xff0c;其他虚拟机都由此克隆而来。因为最初只配置了8G的虚拟硬盘&#xff0c;因此在需要占用比较大的空间的应用时&#xff0c;就比较麻烦。度娘等中文搜索结果没找到答案&#xff0c;只能google了。 这里…