




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】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. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 中药透析技术操作规范
- 德隆集团行业研究
- 合租房财产分配协议
- 商品质量检查及复核合同(2篇)
- 内部转让协议
- 2025年统编版小学道德与法治二年级下册《挑战第一次》说课课件
- 拍卖活动反馈协议
- 学校防汛知识演练
- 阿合奇县2025年数学四年级第二学期期末检测试题含解析
- 陇南师范高等专科学校《英汉互译》2023-2024学年第一学期期末试卷
- 后腹腔镜下输尿管切开取石术课件
- 与装修人员签安全协议书
- 2023年湖北省武汉市中考英语真题(含答案)
- 全面地476种食物升糖指数一览表
- 自然交易理论基础与进阶(自然交易理论丛书)
- (完整版)一年级100以内两位数加一位数的进位加法练习题
- 天冬中药材种植可行性研究报告
- 肝肾综合征演示文稿
- 国际关系理论智慧树知到答案章节测试2023年外交学院
- 1.罂粟碱-经典扩血管药物
- 配料记录表(标准样本)
评论
0/150
提交评论