【移动应用开发技术】Android UI绘制流程的示例分析_第1页
【移动应用开发技术】Android UI绘制流程的示例分析_第2页
【移动应用开发技术】Android UI绘制流程的示例分析_第3页
【移动应用开发技术】Android UI绘制流程的示例分析_第4页
【移动应用开发技术】Android UI绘制流程的示例分析_第5页
已阅读5页,还剩3页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】AndroidUI绘制流程的示例分析

这篇文章主要介绍AndroidUI绘制流程的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、绘制流程源码路径1、Activity加载ViewRootImplActivityThread.handleResumeActivity()

-->

WindowManagerImpl.addView(decorView,

layoutParams)

-->

WindowManagerGlobal.addView()2、ViewRootImpl启动View树的遍历ViewRootImpl.setView(decorView,

layoutParams,

parentView)

-->ViewRootImpl.requestLayout()

-->scheduleTraversals()

-->TraversalRunnable.run()

-->doTraversal()

-->performTraversals()(performMeasure、performLayout、performDraw)二、View绘制流程1、measure(1)MeasureSpec是什么?重写过onMeasure()方法都知道,测量需要用到MeasureSpec类获取View的测量模式和大小,那么这个类是怎样存储这两个信息呢?留心观察的话会发现,onMeasure方法的两个参数实际是32位int类型数据,即:0000000000000000000000000000000000000000000000000000000000000000而其结构为mode+size,前2位为mode,而后30位为size。==>getMode()方法(measureSpec-->mode):private

static

final

int

MODE_SHIFT

=

30;

//

0x3转换为二进制即为:11

//

左移30位后:11000000

00000000

00000000

00000000

private

static

final

int

MODE_MASK

=

0x3

<<

MODE_SHIFT;

public

static

int

getMode(int

measureSpec)

{

//

与MODE_MASK按位与运算后,即将低30位清零,结果为mode左移30位后的值

return

(measureSpec

&

MODE_MASK);

}getSize()方法同理。==>makeMeasureSpec()方法(mode+size-->measureSpec):public

static

int

makeMeasureSpec(

@IntRange(from

=

0,

to

=

(1

<<

MeasureSpec.MODE_SHIFT)

-

1)

int

size,

@MeasureSpecMode

int

mode)

{

if

(sUseBrokenMakeMeasureSpec)

{

return

size

+

mode;

}

else

{

return

(size

&

~MODE_MASK)

|

(mode

&

MODE_MASK);

}

}这里解释一下,按位或左侧为size的高2位清零后的结果,右侧为mode的低30位清零后的结果,两者按位或运算的结果正好为高2位mode、低30位size,例:01000000

00000000

00000000

00000000

|

00001000

00001011

11110101

10101101

=

01001000

00001011

11110101

10101101二进制计算规则可参考:/article/166892.htm==>测量模式:public

static

final

int

UNSPECIFIED

=

0

<<

MODE_SHIFT;

public

static

final

int

EXACTLY

=

1

<<

MODE_SHIFT;

public

static

final

int

AT_MOST

=

2

<<

MODE_SHIFT;UNSPECIFIED:父容器不对View作任何限制,系统内部使用。EXACTLY:精确模式,父容器检测出View大小,即为SpecSize;对应LayoutParams中的match_parent和指定大小的情况。AT_MOST:最大模式,父容器指定可用大小,View的大小不能超出这个值;对应wrap_content。(2)ViewGroup的测量流程回到ViewRootImpl的performMeasure方法,这里传入的参数为顶层DecorView的测量规格,其测量方式为:private

static

int

getRootMeasureSpec(int

windowSize,

int

rootDimension)

{

int

measureSpec;

switch

(rootDimension)

{

case

ViewGroup.LayoutParams.MATCH_PARENT:

measureSpec

=

MeasureSpec.makeMeasureSpec(windowSize,

MeasureSpec.EXACTLY);

break;

case

ViewGroup.LayoutParams.WRAP_CONTENT:

measureSpec

=

MeasureSpec.makeMeasureSpec(windowSize,

MeasureSpec.AT_MOST);

break;

default:

measureSpec

=

MeasureSpec.makeMeasureSpec(rootDimension,

MeasureSpec.EXACTLY);

break;

}

return

measureSpec;

}match_parent和具体数值大小为EXACTLY模式,wrap_content则为AT_MOST模式。往下走,performMeasure方法中调用了DecorView的onMeasure方法,而DecorView继承自FrameLayout,可以看到FL的onMeasure方法中调用了measureChildWithMargins方法,并传入自身的测量规格:protected

void

measureChildWithMargins(View

child,

int

parentWidthMeasureSpec,

int

widthUsed,

int

parentHeightMeasureSpec,

int

heightUsed)

{

final

MarginLayoutParams

lp

=

(MarginLayoutParams)

child.getLayoutParams();

final

int

childWidthMeasureSpec

=

getChildMeasureSpec(parentWidthMeasureSpec,

mPaddingLeft

+

mPaddingRight

+

lp.leftMargin

+

lp.rightMargin

+

widthUsed,

lp.width);

final

int

childHeightMeasureSpec

=

getChildMeasureSpec(parentHeightMeasureSpec,

mPaddingTop

+

mPaddingBottom

+

lp.topMargin

+

lp.bottomMargin

+

heightUsed,

lp.height);

child.measure(childWidthMeasureSpec,

childHeightMeasureSpec);

}即测量子控件的大小,测量规则详情可看getChildMeasureSpec方法,总结如下:回到onMeasure方法,测完子控件之后,ViewGroup会经过一些计算,得出自身大小://

加上padding

maxWidth

+=

getPaddingLeftWithForeground()

+

getPaddingRightWithForeground();

maxHeight

+=

getPaddingTopWithForeground()

+

getPaddingBottomWithForeground();

//

检查是否小于最小宽度、最小高度

maxHeight

=

Math.max(maxHeight,

getSuggestedMinimumHeight());

maxWidth

=

Math.max(maxWidth,

getSuggestedMinimumWidth());

//

检查Drawable的最小高度和宽度

final

Drawable

drawable

=

getForeground();

if

(drawable

!=

null)

{

maxHeight

=

Math.max(maxHeight,

drawable.getMinimumHeight());

maxWidth

=

Math.max(maxWidth,

drawable.getMinimumWidth());

}

setMeasuredDimension(resolveSizeAndState(maxWidth,

widthMeasureSpec,

childState),

resolveSizeAndState(maxHeight,

heightMeasureSpec,

childState

<<

MEASURED_HEIGHT_STATE_SHIFT));综上,ViewGroup的测量需要先测量子View的大小,而后结合padding等属性计算得出自身大小。(3)View的测量流程View.performMeasure()

-->onMeasure(int

widthMeasureSpec,

int

heightMeasureSpec)

-->setMeasuredDimension(int

measuredWidth,

int

measuredHeight)

-->setMeasuredDimensionRaw(int

measuredWidth,

int

measuredHeight)可以看到setMeasuredDimensionRaw()方法:private

void

setMeasuredDimensionRaw(int

measuredWidth,

int

measuredHeight)

{

//

存储测量结果

mMeasuredWidth

=

measuredWidth;

mMeasuredHeight

=

measuredHeight;

//

设置测量完成的标志位

mPrivateFlags

|=

PFLAG_MEASURED_DIMENSION_SET;

}View不需要考虑子View的大小,根据内容测量得出自身大小即可。另外,View中的onMeasure方法中调用到getDefaultSize方法:protected

void

onMeasure(int

widthMeasureSpec,

int

heightMeasureSpec)

{

setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),

widthMeasureSpec),

getDefaultSize(getSuggestedMinimumHeight(),

heightMeasureSpec));

}

public

static

int

getDefaultSize(int

size,

int

measureSpec)

{

int

result

=

size;

int

specMode

=

MeasureSpec.getMode(measureSpec);

int

specSize

=

MeasureSpec.getSize(measureSpec);

switch

(specMode)

{

case

MeasureSpec.UNSPECIFIED:

result

=

size;

break;

case

MeasureSpec.AT_MOST:

case

MeasureSpec.EXACTLY:

//

最终测量的结果都是父容器的大小

result

=

specSize;

break;

}

return

result;

}这里看到精确模式和最大模式,最终测量的结果都是父容器的大小,即布局中的wrap_content、match_parent以及数值大小效果都一样,这也就是自定义View一定要重写onMeasure方法的原因。2、layout布局相对测量而言要简单许多,从ViewRootImpl的performLayout方法出发,可以看到其中调用了DecorView的layout方法://

实则为DecorView的left,

top,

right,

bottom四个信息

host.layout(0,

0,

host.getMeasuredWidth(),

host.getMeasuredHeight());进入layout方法,发现l、t、r、b被传递到了setFrame方法中,并设置给了成员变量:mLeft

=

left;

mTop

=

top;

mRight

=

right;

mBottom

=

bottom;所以,布局实际为调用View的layout方法,设置自身的l、t、r、b值。另外,layout方法中往下走,可以看到调用了onLayout方法,进入后发现为空方法。因而查看FrameLayout的onLayout方法:@Override

protected

void

onLayout(boolean

changed,

int

left,

int

top,

int

right,

int

bottom)

{

layoutChildren(left,

top,

right,

bottom,

false

/*

no

force

left

gravity

*/);

}

void

layoutChildren(int

left,

int

top,

int

right,

int

bottom,

boolean

forceLeftGravity)

{

final

int

count

=

getChildCount();

//

省略

for

(int

i

=

0;

i

<

count;

i++)

{

final

View

child

=

getChildAt(i);

if

(child.getVisibility()

!=

GONE)

{

final

LayoutParams

lp

=

(LayoutParams)

child.getLayoutParams();

//

省略

child.layout(childLeft,

childTop,

childLeft

+

width,

childTop

+

height);

}

}

}可以看到,进行一系列计算后,调用了child的layout方法,对子控件进行布局,同时子控件又会继续往下对自己的子控件布局,从而实现遍历。综上,布局实际为调用layout方法设置View位置,ViewGroup则需要另外实现onLayout方法摆放子控件。3、d

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论