自定义view与viewGroup

他俩的区别就在于重写其中的onMeasure、onlayout、ondraw三个方法 viewgroup大部分情况不需要绘制,而view不需要layout。

在自定义view的过程中,大部分都是在draw,涉及到重写点击事件与绘制canvas+paint。
点击事件中,view就只有touch了,不需要分发与拦截了,但是有必要时需要请求父容器放开拦截,这就是内部拦截法。另外注意一点,down的事件是谁处理的后续的move也是谁处理,所以处理事件冲突时,你看着移出界面了,但是实际上是后台帮你做好了,帮你把view的事件做了迁移,重新分发到了其他的view,才进行了后续的事件处理。
然后是canvas,所有的画布都是一块儿,都是针对画布的操作,其中有一个部分就是canvas的save与 restore,保存前面的状态。当你该了画笔内容或者画布坐标系,或者画笔起始点,不会影响以前的设置。

那viewgroup,就是对onmeasure与onLayout了,onMeasure是测量自己以及自己的孩子,而onlayout则是通过child.layout控制自己孩子的位置。那么这里有个padding与margin的区别,自己的padding要自己处理,孩子的margin也要自己处理,才能确定child的layout的位置。

还要点击事件的分发,分发之前,都要询问自身是否拦截,那一般都是不拦截的,之间便利子view,看坐标是在哪个view上,就分给它去处理,否则自己处理。最后若是子view也没有处理,还是得返回来问自己是不是要处理,并返回上去。这里的也就是外部拦截法的位置,比较简单,通过坐标,控制是否拦截即可。

那还有,以上可以看到都是先dispatch再ontouch,那么activity中也有着几个方法的重写,又是什么关系呢?打印日志可以看到,都是先activity中的diapath先触发,再到viewgroup,若是消费完,倒也罢了,否则会回到activity的ontouch事件,并进入ontouchevent,ontouchevent的返回值默认为false,表示未消费,若是为true,就没法分发到click等其他消费事件了,要注意。

android自定义ViewGroup加在自定义布局 自定义view和自定义viewgroup的区别_自定义view


如上所示,正常的 从activity开始,到viewgroup到button的dispatch,最终还会回调到 我们在activity中setOntouchListener中,然后是本身view的onTouchEvent重写。注意到了吗,这个activity与view之间交互挺深的,虽然我们把listener写入了activity中,但是实际上回调赋予还是在PhoneWindow中,当分发事件时,会将事件分发给这些监听,总而言之,桥接模式。

还有注意到,没有触发activity的onTouchevent,因为事件被消费了,若是没有消费,就会触发这个event。

讲到这里,其实很明显activity +window+view才是一个整体,但是大部分简单面试中都舍弃了window这块儿。所以wms这个东西,先放一放吧,这东西跟底层太有联系了。因为大部分事件实际是在window这块处理的,而不是想象中的activity。写在activity的只是回调。真正调他的还是在window中。

总结一个activity+window+view的过程

Acivity在onResume之后才显示的原因是什么

虽然我们设置Activity的布局一般都是在onCreate方法里调用setContentView。里面是直接调用Window的setContentView,创建一个DecorView用来包住我们创建的布局,详情如下:

PhoneWindow.java #setContentView
 // 加载布局,添加到mContentParent ,mContentParent又是DecorView的一个子布局。
 mLayoutInflater.inflate(laoutResId,mContentParent);


然而这一步只是加载好了布局,生成了一个ViewTree,具体怎么把ViewTree显示出来,答案在下面:

ActivityThread #handleResumeActivity
 // onResume回调方法。
 ActivityClientRecord f=performResumeActivity()
 final Activity a=r.activity; //看到了吗activity也是个被使用的对象,在record里面存着
 if (r.window == null && !a.mFinished && willBeVisible) {
 r.window = r.activity.getWindow();
 View decor = r.window.getDecorView();
 decor.setVisibility(View.INVISIBLE);
 ViewManager wm = a.getWindowManager();
 WindowManager.LayoutParams l = r.window.getAttributes();
 a.mDecor = decor;
 if (a.mVisibleFromClient) {
 if (!a.mWindowAdded) {
 a.mWindowAdded = true;
 wm.addView(decor, l);


直到这里,才把decor添加到window,windowManager的addView方法最终将DecorView添加到WMS,
至于实现绘制到屏幕、接受触屏事件。具体的调用则是从这个addView开始
WindowManagerImpl.addView
->WindowManagerGlobal.addView
->ViewRootImpl.setView
->ViewRootImp;.requestLayout //执行View的绘制流程
// 通过Binder调用WMS,WMS会添加一个Window相关对象。
// 应用端通过mWindowSession 调用WMS
// WMS通过mWindow(一个)

如何触发view的绘制呢,还有requestLayout有啥用呢
下一篇总结handler吧。

更多自定义view的绘制相关,找这里:http://www.gcssloop.com/category/customview.html
虽然很老了,但是写的很不错哦