View(1) - 绘制原理

体系

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

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值