这一期我们讲解自定义View的重量级的概念,View。那么什么是View?View,顾名思义,就是视图,就是显示在手机屏幕上的应用界面。
因为我们是面向对象开发,所以处处皆对象。你所看到的界面都是由一个个View嵌套组合而成的。这里就引入一个概念,View Tree,它是一个树状结构,这里我们就想到了组合模式。没错,View的子类ViewGroup就是树枝节点,而View就是树叶节点。
每一个Activity都对应一个窗体,Window,它实际上是一个PhoneWindow。它的根布局是PhoneWindow.DecorView,继承自FrameLayout。它里面就放了我们应用的状态栏、布局内容和导航栏。中间那部分是一个FrameLayout,这个控件的id是android:id/content。我们在Activity中setContentView(view)的那个view就是直接添加到这个FrameLayout之下的。
我们再讲讲我们如果要自己定义一个View,需要重写哪些方法?对了,onMeasure和onDraw。
那么什么是onMeasure呢?onMeasure顾名思义就是测量的意思,它来决定你自定义的这个View要绘制的尺寸。在onMeasure的最后,调用setMeasuredDimension()方法来确定测量后的结果。在绘制视图之前,调用getMeasuredWidth和getMeasuredHeight就可以拿到测量的结果。而在View绘制完之后,也就是Activity调用onResume之后,才能拿到getWidth和getHeight的值。这就是这两组方法的区别。那么回到onMeasure,我们了解到有一个重要的类,MeasureSpec,测量规格。它就是用来编码和解码宽高数据的。通过MeasureSpec.makeMeasureSpec(int size, int mode)来组合一个测量的规格,通过MeasureSpec.getMode和MeasureSpec.getSize,解析出测量模式和测量尺寸。mode有三种,size就是给你绘制参考的值。MeasureSpec的模式如下:
MeasureSpec.UNSPECIFIED //未指定
MeasureSpec.EXACTLY //确切的
MeasureSpec.AT_MOST //至多
UNSPECIFIED就是说这个值会在运行时反复修改,比如ListView就是这个MeasureSpec。EXACTLY意思是说在xml指定的android:layout_width或android:layout_height是match_parent或像15dp这样的明确的值,而AT_MOST表示xml指定的是一个wrap_content。如果指定wrap_content,我们需要结合控件的业务需求,给一个合理的默认值。让使用者即使设置wrap_content的使用,也能显示的没有毛病。
onDraw就是View绘制的关键所在了。在这里,你可以通过Paint将各种各样的形状以及图形绘制到Canvas上,具体回顾之前讲过的Canvas和Paint的用法。
View有3种可见度状态,View.VISIBLE、View.GONE和View.INVISIBLE。VISIBLE就是View是可见的,这里不多说。GONE和INVISIBLE都是不可见的,它们的区别在于INVISIBLE只是隐藏了,它还是占据着布局中的位置的,而GONE是直接连位置都不占据了。
我们调用View的invalidate()方法和postInvalidate()方法都是可以进行控件的刷新的,简单来说,就是会再调用一次onDraw()方法。我们通过改变绘制的一些变量,来达到改变显示内容的目的。invalidate只在主线程中使用,而postInvalidate也可以在子线程中使用。但最终都是让Handler去调度的,因为界面绘制都是由主线程完成的。考虑到这个情况,我们不要在onDraw中new大量的对象,因为会被反复创建,影响性能。
View的源码非常的多,有兴趣的可以去读一读,本文篇幅有限,就不展开了。