做了这么久android,除了数据就是ui。在日常开发中,android原生的控件满足不了万恶的产品的要求,总是需要自定义控件或者自定义view来满足那产品的欲望,在这里小编通过自己的开发经历,来诉说一下自己对自定义view的一些看法和总结!
在这里我先述说个题外话。首先我们启动自己的activity是在开机桌面的基础之上,通过包名启动,用过intent在自己应用程序中启动别人应用。同样的原理,我们的应用是通过桌面的应用通过包名启动。这是我的理解!
言归正传我们来述说我们的整体view!
activity是app的视图窗口,这里我们说下activity的组织结构,activity -----> mwindow,mwindow为phonewindow的一个对象,phonewindow继承于window抽象类,负责窗口的管理,但是呈现效果的是DecorView对象。DecorView是FrameLayout的子类,也是整个view的根!
DecorView是由三部分组成 :ActionBar、标题区和内容区。如下图所示:
在这里特别说明一点:PhoneWindow关联了一个名为mWindowManager的WindowManager的对象,创建了一个ViewRootlmpl对象和WindowManagerService,WindowManagerService能获取各种监听事件,比如触摸事件、键盘事件等,并通过ViewRootImpl将事件分发给各个Activity;
现在我们来说说View树的绘图流程
绘制的开始是从ViewRootImpl的performTraversals()方法开始的,此方法是父类的私有方法,不可重写。在其中提出3个重要方法。
performMeasure();
performLayout();
performDraw();
相信开发android的同行看到这,一定会感到非常眼熟,这就是绘制view3部曲。
在performMeasure()中根据设置的模式计算出组件的宽度和高度,事实上只有当wrap_parent才需要测量!performMeasure中有个onMeasure方法是为组件尺寸测量预留的功能接口,一般情况下都需要重写。ps:如果测量的是容器的尺寸,并且容器的尺寸又依赖于子组件的大小,必须先测量容器中子组件的大小,不然测出的结果为0.这里的测量仅仅是个参考,最终是有setframe()决定的!
在performLayout()中用于确认子组件的位置,仅仅适用于ViewGroup容器类。在layout()方法中定位前需要重新测量组件的大小,需先调用onMeasure(),再执行setFrame();此时保存了相关的值并未绘制,之后在进行调用onlayout接口方法。
在performDraw()中绘制功能,在这里每个组件只负责自身的绘制,容器类一般来说不需要绘制。首先在ondraw方法中调用drawSoftware()方法,绘制组件通过Canvas类完成,其中使用了surface双缓存机制。
View类的draw()方法中做了以下几件事:
1.绘制背景 background.draw(canvas)
2.绘制自己 onDraw(canvas)
3.绘制子视图 dispatchDraw(canvas)
4.绘制滚动条 onDrawScrollBars(canvas)
在这里如果是继承View的组件需要重写onDraw方法,其中可以不需要add任何view,但是如果是继承自ViewGroup的组件就需要重写onLayout和onMeasure,因为ViewGroup本身就是一个容器,需要重新测量。
这里小编总结下组件的绘制过程,通过WindowManager对象创建ViewRootImpl对象和WindowManagerService,之后通过ViewRootImpl中的performMeasure等三个方法进行绘制!
未完待续!!!请等待下篇自定义控件Graphics2D