查看原文
其他

每日一问:谈谈自定义 View 有些什么注意点

nanchen nanchen 2020-10-29

自定义 View 基本是每一名 Android 开发工程师都应该会的技能,其实搞清楚原理后,其实也没有那么困难,大多数童鞋都是直接被自定义 View 多还繁杂的流程所吓住。

对于自定义 View 和属性动画等操作,相信我无论如何也写不出扔物线的 https://hencoder.com/ 的高度,所以并不打算在这个上面浪费口舌。

不过,我们今天倒是可以来探讨一下,平时大家在自定义 View 的时候都有些什么需要我们去注意的。

对于自定义 View

对于大多数自定义 View 来说,如果是要绘制某些东西,直接在 onDraw() 方法中进行绘制即可,如果要处理触摸事件,那么直接重写 onTouchEvent() 并在里面编写自己的逻辑即可。大多数情况下,我们都需要重写 onMeasure(),如果不重写的话,就必须在外面指定好宽高,相信不会有人希望看到 wrap_content 和 match_parent 一样的展示效果吧。

对于 View 的测量的话,我想每一个人都得清楚 MeasureSpec 这个类的 Mode 和 size。对于 onMeasure() 里面的测量比较重要的可以移步到我之前的一篇文章:每日一问:谈谈你对 MeasureSpec 的理解。文章中详细介绍了 MeasureSpec 的测量模式和尺寸。不过文中并没有针对 UNSPECIFIED 这种「想多大就多大」的模式进行详细探究,对于 UNSPECIFIED 相关可以查看这篇文章:每日一问:详细说一下 MeasureSpec.UNSPECIFIED。

至此,我们基本可以总结到:直接继承自 View 的自定义 View 都是建议重写 onMeasure() 方法的,并且我们一定不应该忽略 MeasureSpec.UNSPECIFIED 这个测量模式。因为在可滚动的布局中摆放我们这个自定义 View 的时候,通常需要处理这种「想多大就多大」的情况。

除此之外,我们在自定义 View 的时候,还可以注意的点有:

  1. 如果有自定义属性,在构造方法中及时对 TypeArray 进行 recycler() 操作;

  2. onDraw() 和 onTouchEvent() 方法中尽量避免创建对象或者进行耗时操作,如果过多出现会导致卡顿。

  3. invalidate() 与 postInvalidate() 都用于刷新 View,区别是 invalidate() 可以在主线程调用,而 postInvalidate() 可直接使用在子线程。

  4. 比较重要级的资源要释放的时候可以重写 onDetachedFromWindow() 并进行释放。

对于自定义 ViewGroup

而对于自定义 ViewGroup,这里同样是只探讨直接继承自 ViewGroup 的自定义 ViewGroup。我们在编写的时候,必须重写 onLayout() 并在其中编写子 View 摆放的逻辑,同样我们也就必须重写 onMeasure() 去测量每一个子 View 的尺寸进而来确定自己的尺寸。

在 onMeasure() 中遍历子 View,并显式调用它们的 measure() 方法进而触发子 View 的 onMeasure() 方法得到每一个子 View 的尺寸。如果要处理触摸事件的话,需要重写 onInterceptTouchEvent() 或者 onTouchEvent() 方法。

那么,自定义 ViewGroup 还有哪些需要我们注意的呢?

对于 LayoutParams,我们在前面的 每日一问:关于 LayoutParams,你所应该知道的一文中详细讲解了我们系统的 LayoutParams 的费心费力,讲到了为什么我们不设置甚至胡乱设置 LayoutParams 都不会导致崩溃的原因。所以我们在自定义 ViewGroup 的时候,最好也可以看看系统是怎么处理 LayoutParams 的。实际上,我们在自定义 ViewGroup 的时候最好重写下面四个方法。

  • generateDefaultLayoutParams()
    这个方法主要是在我们通过 addView 添加子 View 到里面的时候,没有添加 LayoutParams 的时候会回调进行处理。

  • generateLayoutParams(attrs: AttributeSet?)
    这个方法主要是用于我们在 inflate() View 的时候需要构造的一个 LayoutParams处理。

  • checkLayoutParams(p: LayoutParams?)
    这个方法主要是判断我们代码中设置的 LayoutParams 是否正确,以防止我们在强转的时候发生异常。

  • generateLayoutParams(p: LayoutParams?)
    这个方法用于在我们外部设置错误的 LayoutParams 时纠正为正确的 LayoutParams

在自定义 ViewGroup 有触摸事件的时候,如果希望不影响子 View 的行为,需要重写 onInterceptTouchEvent() 方法去判断哪些行为是自己需要的,哪些是自己不需要的。

如果想要在 ViewGroup 中绘制一些东西,又没有在布局中设置 background 的时候,需要调用 setWillNotDraw(false) 进行处理。

可以还有许多,但这里由于时间关系就点到为止,总之,千万要注意 MeasureSpec.UNSPECIFIED,这个并没有诸多博客写到的很少见那么不重要。

—————END—————



我是南尘,只做比心的公众号,欢迎关注我。

推荐阅读:

每日一问:SharedPreferences 那些不为人知的秘密(上)

每日一问:SharedPreferences 那些不为人知的秘密(下)


欢迎关注南尘的公众号:nanchen
做不完的开源,写不完的矫情,只做比心的公众号,如果你喜欢,你可以选择分享给大家。如果你有好的文章,欢迎投稿,让我们一起来分享。
          长按上方二维码关注        做不完的开源,写不完的矫情        一起来看 nanchen 同学的成长笔记




    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存