View的绘制
View的绘制流程主要经历3个阶段:测量、布局、绘制
measure:判断是否需要重新计算View的大小 layout:判断是否需要重新计算View的位置 draw:判断是否需要重新绘制View 对应的3个回调是,onMeasure、onLayout、onDraw
measure测量
MeasureSpec是一个32位的整数,由两部分组成,高2位是mode,低30位是size mode有3个值{UNSPECIFIED, EXACTLY, AT_MOST},size就是测量模式下的尺寸 UNSPECIFIED:父View对子View没有任何约束,子View就是达到希望的最大尺寸 EXACTLY:(match_parent或具体的dp)父视图为子视图指定了一个确定的尺寸 AT_MOST:(wrap_content)父视图为子视图划定了一个最大尺寸,这个时候需要子View根据需求自己来计算自己大小,然后告诉父View 在自定义View中重写onMeasure方法,可以获取到mode和seize,然后通过计算可以将修改后的值通过setMeasuredDimension方法传入
@Override
protected void onMeasure ( int widthMeasureSpec, int heightMeasureSpec) {
super . onMeasure ( widthMeasureSpec, heightMeasureSpec) ;
int mode = MeasureSpec. getMode ( widthMeasureSpec) ;
int size = MeasureSpec. getSize ( widthMeasureSpec) ;
setMeasuredDimension ( 720 , 1280 ) ;
}
layout摆放
确定View在父布局中的摆放位置 当View的位置发生变化时,会调用onLayout方法
@Override
protected void onLayout ( boolean changed, int left, int top, int right, int bottom) {
super . onLayout ( changed, left, top, right, bottom) ;
}
draw绘制
绘制自己 通知子View去绘制(只有ViewGroup会dispatchDraw,View的dispatchDraw方法是空的)
public void draw ( Canvas canvas) {
.. .
onDraw ( canvas) ;
dispatchDraw ( canvas) ;
.. .
}
可以重写onDraw方法来自定义实现View的绘制内容
@Override
protected void onDraw ( Canvas canvas) {
super . onDraw ( canvas) ;
}
补充知识
当Activity接收到焦点的时候就会被请求绘制UI布局,由FragmeWork层来完成。 Window窗口独占一个Surface实例 PhoneWindow是Window的唯一实现类 用户界面通过PhoneWindow来承载 View是一个树的结构,它会从跟节点开始测量绘制,View树的绘制都是在代码ViewRootImpl中绘制的 当建立起Decorview和ViewRoot的关联后,就会调用requestLayout方法请求首次绘制,在scheduleTraversals中通过来向Handler向主线程中发送消息,依次遍历来完成每次的绘制
public void requestLayout ( ) {
if ( ! mHandlingLayoutInLayoutRequest) {
checkThread ( ) ;
mLayoutRequested = true ;
scheduleTraversals ( ) ;
}
}
.. .
void scheduleTraversals ( ) {
if ( ! mTraversalScheduled) {
mTraversalScheduled = true ;
mTraversalBarrier = mHandler. getLooper ( ) . getQueue ( ) . postSyncBarrier ( ) ;
mChoreographer. postCallback (
Choreographer. CALLBACK_TRAVERSAL, mTraversalRunnable, null ) ;
notifyRendererOfFramePending ( ) ;
pokeDrawLockIfNeeded ( ) ;
}
}