体系
Activity -> phoneWindow ->DecorView -> 各ViewGroup等
1.DecorView(FrameLayout)包含StateView、TitileView、ContentView 等子View ,setContentView 设置的是DecorView子View。
2.Activity 托管 phoneWindow实例对象,phoneWindow管理DecorView。因此在Activity中可以getWindow().DecorView得到顶层View。
3.decorView https://blog.csdn.net/guxiao1201/article/details/41744107
分类
TextView - Button EditText
ImageView - ImageButton
ProgressView
ViewGroup - map和6大布局组件 (ScrowView继承FrameLayout WebView继承AbsultLayout)
SurefaceView - videoView
ViewStub
绘制原理
视图绘制的起点在ViewRootImpl类的performTraversals()方法,该方法主要工作: 根据之前的状态,判定是否重新计算测试视图大小(measure)、是否重新放置视图位置(layout)、是否重新重绘视图(draw)
1.measure() 递归测量组件本身的大小
MeasureSpec是一个组合尺寸,它是一个32位bit值,高两位是尺寸模式specMode,低30位是尺寸大小值
specMode有三种: MeasureSpec.EXACTLY表示确定大小, MeasureSpec.AT_MOST表示最大大小, MeasureSpec.UNSPECIFIED不确定
int measureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); //合成
int specMode = MeasureSpec.getMode(measureSpec); //拆解
int specSize = MeasureSpec.getSize(measureSpec);
使用View的getMeasuredWidth()和getMeasuredHeight()方法来获取View测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值
2.layout() 确定组件在视图中的位置
measure操作完成后得到的是对每个View经测量过的measuredWidth和measuredHeight,layout操作完成之后得到的是对每个View进行位置分配后的mLeft、mTop、mRight、mBottom,这些值都是相对于父View来说的
使用View的getWidth()和getHeight()方法来获取View测量的宽高,必须保证这两个方法在onLayout流程之后被调用才能返回有效值。
3.draw() 根据位置和大小,将组件画出来
如果该View是一个ViewGroup,则需要递归绘制其所包含的所有子View。
View默认不会绘制任何内容,真正的绘制都需要自己在子类中实现。
绘制步骤:
- 首先绘制View的背景;
- 如果需要的话,保持canvas的图层,为fading做准备;
- 然后,绘制View的内容;
- 接着,绘制View的子View;
- 如果需要的话,绘制View的fading边缘并恢复图层;
- 最后,绘制View的装饰(例如滚动条等等)。
默认情况下子View的ViewGroup.drawChild绘制顺序和子View被添加的顺序一致,但是你也可以重载ViewGroup.getChildDrawingOrder()方法提供不同顺序。
View常用方法
1.invalidate、postInvalidate()请求当前view重新绘制
- invalidate主线程调用
- postInvalidate非主线程调用
2.requestLayout:
requestLayout会直接递归调用父窗口的requestLayout,递归一直到ViewRootImpl,然后触发peformTraversals,导致onMeasure和onLayout被调用。不一定会触发OnDraw。requestLayout触发onDraw可能是因为在在layout过程中发现l,t,r,b和以前不一样,那就会触发一次invalidate,所以触发了onDraw,也可能是因为别的原因导致mDirty非空(比如在跑动画)调用addView、setVisbility、setText方法都会触发requestLayout
3.scrollTo()、scrollBy()
scrollBy内部是调用的scrollTo ,scrollBy移动的坐标是相对当前控件的坐标地址,而scrollTo是移动当前控件的绝对地址。(正数向左或向上 负数向右或向下移动)
4.在Activity中获取某个View的宽高
- Activity/View#onWindowFocusChanged:此时View已经初始化完毕,当Activity的窗口得到焦点和失去焦点时均会被调用一次,如果频繁地进行onResume和onPause,那么onWindowFocusChanged也会被频繁地调用。
- view.post(runnable): 通过post可以将一个runnable投递到消息队列的尾部,始化好了然后等待Looper调用次runnable的时候,View也已经初始化好了。
- ViewTreeObserver#addOnGlobalLayoutListener:当View树的状态发生改变或者View树内部的View的可见性发生改变时,onGlobalLayout方法将被回调。
- View.measure(int widthMeasureSpec, int heightMeasureSpec):match_parent时不知道parentSize的大小,测不出;具体数值时,直接makeMeasureSpec固定值,然后调用view..measure就可以了;wrap_content时,在最大化模式下,用View理论上能支持的最大值去构造MeasureSpec是合理的。
参考:https://www.jianshu.com/p/5ae3356014ed