美文网首页Android开发Android进阶之路Android开发
Android学习“易错” 系列:老司机都掉的坑,你进去了吗?

Android学习“易错” 系列:老司机都掉的坑,你进去了吗?

作者: 程序员的Vere | 来源:发表于2020-03-31 17:38 被阅读0次

之前分享了很多面试题,蛮多都自带正确答案,来几期易错系列,大家一起醒醒神。

今天来个简单的开胃一下。

这个知识点,我定义为在面试过程中答对不加分,答错扣分的题目,不过在我以前面试经历中,能完整说上来的同学不多。

我们一起来看看大家对这个知识的掌握程度吧。

在早期的博客的里面,很多时候,见到有如下的介绍:

  • 如果你的 View设置了 match_parent,则在onMeasure 中得到的测量模式为:EXACTLY;
  • 如果设置了wrap_conent,则对应测量模式为:AT_MOST
  • 还剩下一个 UNSPECIFIED大家不用管,不常用;

上述描述每句话都可以认为是错的。

那么今天我要搞清楚几个问题:

  1. match_parent / wrap_conent一定对应EXACTLY/ AT_MOST 吗 ?
  2. 测量模式到底是由哪些因素确定的?
  3. UNSPECIFIED 真的不常见吗?

1. match_parent和wrap_content就一定对应MeasureSpec.EXACTLYMeasureSpec.AT_MOST吗?

肯定不是。

为什么呢?

因为Viewmeasure时,它的宽高MeasureSpec完全是取决于父容器,父容器传的是什么它收到的就是什么。

如果这个父容器的onMeasure方法里面写死了每个子ViewMeasureSpecModeUNSPECIFIED的话,那么无论你在xml布局或者LayoutParams中怎么设置宽高都好,最终子ViewonMeasure收到的也是UNSPECIFIED

好吧,故意手动指定的不算。

就以正常的角度来看:

我们都知道,自定义ViewGroup过程中,需要在onMeasure里面对子View进行测量。

在测量子View时,往往会通过measureChildmeasureChildWithMargins方法来完成(比如FrameLayoutLinearLayoutCoordinatorLayoutViewPager2)。

或者调用ViewGroup的静态方法getChildMeasureSpec来直接获取目标子ViewMeasureSpec,然后手动measure(比如ScrollViewNestedScrollViewDrawerLayoutTabLayoutConstraintLayout)。

其实,measureChildmeasureChildWithMargins里面也是会通过getChildMeasureSpec方法来获取MeasureSpec的,也就是说,上面提到的这些容器,在测量它们的子View之前,都是先通过getChildMeasureSpec方法来获取子View的宽高MeasureSpec,然后传给子Viewmeasure方法的。

好,那我们现在来看看getChildMeasureSpec方法里面做了什么:

  public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
      int specMode = MeasureSpec.getMode(spec);
      ......
      switch (specMode) {
          case MeasureSpec.EXACTLY:
              if (childDimension >= 0) {
                  ......
                  resultMode = MeasureSpec.EXACTLY;
              } else if (childDimension == ViewGroup.LayoutParams.MATCH_PARENT) {
                  ......
                  resultMode = MeasureSpec.EXACTLY;
              } else if (childDimension == ViewGroup.LayoutParams.WRAP_CONTENT) {
                  ......
                  resultMode = MeasureSpec.AT_MOST;
              }
              break;

          case MeasureSpec.AT_MOST:
              if (childDimension >= 0) {
                  ......
                  resultMode = MeasureSpec.EXACTLY;
              } else if (childDimension == ViewGroup.LayoutParams.MATCH_PARENT) {
                  ......
                  resultMode = MeasureSpec.AT_MOST;
              } else if (childDimension == ViewGroup.LayoutParams.WRAP_CONTENT) {
                  ......
                  resultMode = MeasureSpec.AT_MOST;
              }
              break;

          case MeasureSpec.UNSPECIFIED:
              if (childDimension >= 0) {
                  ......
                  resultMode = MeasureSpec.EXACTLY;
              } else if (childDimension == ViewGroup.LayoutParams.MATCH_PARENT) {
                  ......
                  resultMode = MeasureSpec.UNSPECIFIED;
              } else if (childDimension == ViewGroup.LayoutParams.WRAP_CONTENT) {
                  ......
                  resultMode = MeasureSpec.UNSPECIFIED;
              }
              break;
      }
      return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
  }

可以看到:

父容器的specMode为EXACTLY时,一切正常(子View尺寸指定为match_parent或精确的dimen值时,Mode = EXACTLY,尺寸指定为wrap_contentMode = AT_MOST);

父容器specMode为AT_MOST的时候,呵呵,可以看到,除了指定了dimen值之外,无论设置为match_parentwrap_contentMode最终都是会变成AT_MOST

如果父容器specModeUNSPECIFIED的话,跟上面的逻辑差不多,都是会变成UNSPECIFIED的,除非指定了精确的dimen值;

所以,ViewonMeasure方法中收到的宽高MeasureSpec,不完全是由xml布局中设置的宽高或LayoutParams的宽高值决定的。

2. 有哪些因素影响着MeasureSpec的mode?

从刚刚的getChildMeasureSpec方法中可以看出,影响着View测量模式的因素主要是该View所属容器的测量模式。

也就是说,正常情况下(不是故意乱设置),View的测量模式是由:

**它自身的LayoutParams设置的值 **+ 父容器的测量模式来决定的。

为什么大家都说MeasureSpec.UNSPECIFIED不常见呢?

大家都觉得这个模式不常见,很可能就是因为在编写布局时,View的宽高只能选择match_parentwrap_content或者直接指定一个精确的尺寸,相对来说,MeasureSpec.UNSPECIFIED就显得不太透明了,因为在日常开发中,如不需定制View的话,基本上不会直接接触到。

3. MeasureSpec.UNSPECIFIED是不是真的不常见?

在日常定制View时,确实很少会专门针对这个模式去做特殊处理,大多数情况下,都会把它当成MeasureSpec.AT_MOST一样看待,就比如最最常用的TextView,它在测量时也是不会区分UNSPECIFIED和AT_MOST的。

不过,虽说这个模式比较少直接接触到,但很多场景下,我们已经在不知不觉中用上了,比如RecyclerViewItem,如果Item的宽/高是wrap_content且列表可滚动的话,那么Item的宽/高的测量模式就会是UNSPECIFIED

还有就是NestedScrollViewScrollView,因为它们都是扩展自FrameLayout,所以它们的子View会测量两次,第一次测量时,子ViewheightMeasureSpec的模式是写死为UNSPECIFIED的。

我们在自定义ViewGroup过程中,如果允许子View的尺寸比ViewGroup大的话,在测量子View时就可以把Mode指定为UNSPECIFIED

好了,希望这次你彻底弄明白了自定义控件的测量模式相关知识。

另外也有人给我发了个图,说这个图就能说明白了,其实这个图也有一点点小问题:

我画圈的地方,这个值不一定是 0, 不过大多情况下 UNSPECIFIED这个模式一般不在乎这个size

最后

今天就讲到这里,我的Android核心技术学习大纲,获取相关内容来我的GitHub一起玩耍:https://github.com/Meng997998/AndroidJX

对于进阶这条路而言,学习是会有回报的!

你把你的时间投资在学习上,就意味着你可以收获技能,更有机会增加收入。

分享我的Android学习PDF大全
这份Android学习PDF大全真的包含了方方面面了,内含Java基础知识点、Android基础、Android进阶延伸、算法合集等等

我的这份学习合集,可以有效的帮助大家掌握知识点。

总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

Android学习PDF大全关注我看个人介绍,或者简信我获取

相关文章

网友评论

    本文标题:Android学习“易错” 系列:老司机都掉的坑,你进去了吗?

    本文链接:https://www.haomeiwen.com/subject/cqcguhtx.html