NumberPicker分析(二)

news/2024/6/30 18:28:29

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://lihuaxi.xjx100.cn/news/1052862.html

相关文章

K8s集群搭建-Kubeadm方式搭建集群【1.23.0版本】

文章目录 一、初始化准备二、安装kubeadm三、初始化Master集群四、将新的Node节点加入集群五、部署CNI网络插件六、其他配置 Kubernetes1.24(包括1.24)之后不在兼容docker,如果有需要兼容docker的需求&#xff0c;则安装一个 cri-docker的插件&#xff0c;本文使用的是kuberne…

混淆电路(GC)

基本概念 在混淆电路框架下&#xff0c;任意功能函数可被表示为一个与门和异或门组成的布尔电路&#xff0c;协议的参与方由生成方&#xff08;Garbler&#xff09;和计算方&#xff08;Evaluator&#xff09;组成。 **大致的流程&#xff1a;**生成方生成密钥并加密查找表&am…

codemirror 5前端代码编辑器资料整理。

CodeMirror 是基于js的源代码编辑器组件&#xff0c;它支持javascript等多种高级语言&#xff0c;tampermonkey内置的代码编辑器就是基于它。它的按键组合方式兼容vim&#xff0c;emacs等&#xff0c;调用者还可自定义”自动完成“的列表窗口&#xff0c;自由度极高&#xff0c…

【测试开发】第一节.测开入门(附常考面试题)

文章目录 前言 一、什么是测试开发 1.1 常考面试题 二、软件测试的基础概念 2.1 需求 2.2 测试用例 3、BUG 三、生命周期 3.1 软件的生命周期 3.2 软件测试的生命周期 四、软件工程中的几种常见的开发模型 4.1 瀑布模型 4.2 螺旋模型 4.3 增量模型和迭代模型 4.4 敏捷…

基于matlab评估机场监控雷达上 5G 新无线电 (NR) 信号的干扰

一、前言 随着5G NR系统的频率范围超出LTE中使用的频段&#xff0c;频谱管理变得更加复杂。对扩大5G覆盖范围的需求是由更高的数据速率和更低的延迟的好处推动的。新5G基站的实施反过来又推动了了解这些信号如何影响在相同频段上运行的已安装系统的需求。其中一个系统是空中交通…

无线洗地机哪款性价比高?高性价比的洗地机分享

虽说现在市面上清洁工具很多&#xff0c;但是要说清洁效果最好的&#xff0c;肯定非洗地机莫属。它集合了吸&#xff0c;洗&#xff0c;拖三大功能&#xff0c;干湿垃圾一次清理&#xff0c;还能根据地面的脏污程度进行清洁&#xff0c;达到极致的清洁效果&#xff0c;省时省力…

【STM32】基础知识 第五课 C 语言基础知识

【STM32】基础知识 第五课 C 语言基础知识 stdint.h 简介位操作寄存器位赋值 宏定义带参数的宏定义条件编译头文件编译代码条件编译extern 声明 类别名 (typedef)结构体 指针指针使用的常见问题代码规范 stdint.h 简介 stdint.h 是从 C99 中引进的一个标准 C 库的文件. 路径: …

前向传播的简单介绍,并给出代码实例

文章目录 前向传播的介绍前向传播的基本概念前向传播的步骤实例代码示例一代码示例二定义模型定义损失函数定义优化器执行前向传播 总结 前向传播的介绍 前向传播是神经网络中的一种基本操作&#xff0c;其作用是将输入数据通过网络中的权重和偏置计算&#xff0c;最终得到输出…