详解Android布局加载流程源码的攻略如下:
1. 确定需求
在详细讲解Android布局加载流程源码之前,需要先了解本次攻略的目的和需求。本次攻略的目的是通过对Android布局加载流程源码的分析,深入了解Android布局加载的过程和原理,以便进一步优化相关的开发工作。
2. 学习布局加载流程
2.1 布局加载的基本流程
Android布局加载的基本流程分为3个阶段,即解析XML文件、创建View树和测量、布局和绘制View。其中,解析XML文件的过程是由LayoutInflator类实现的,创建View树和测量、布局和绘制View的过程则是由ViewGroup类和View类实现的。
2.2 解析XML文件的源码分析
LayoutInflator类的主要作用是将XML文件解析成View对象,其源码分析过程如下:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
final LayoutInflaterDelegate delegate = this.mDelegate;
if (delegate != null) {
return delegate.onCreateView(null, null, parser);
}
synchronized (mConstructorArgs) {
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = mContext;
View result = root;
try {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty loop.
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
final String name = parser.getName();
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("merge can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, mContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
View temp = rInflate(parser, root, mContext, attrs, false);
if (root != null && attachToRoot) {
root.addView(temp);
}
result = temp;
}
} finally {
mConstructorArgs[0] = lastContext;
}
return result;
}
}
在该源码中,首先获取了解析器的属性集,然后通过一个while循环遍历XML文件,直到找到根元素(即XML文件的第一个元素)。接着,通过调用rInflate()方法创建View树。在创建View树过程中,如果该XML文件根元素设有merge
标签,则需要结合父View来创建View树,否则直接为根元素创建View树。最后,如果attachToRoot
参数为true
,则将创建的View加入到根ViewGroup中。
2.3 创建View树和测量、布局和绘制View的源码分析
创建View树和测量、布局和绘制View的过程由ViewGroup类和View类共同完成,其源码分析过程如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//遍历子View,对View进行测量
measureChildren(widthMeasureSpec, heightMeasureSpec);
//设置自身宽高
int width = 0;
int height = 0;
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
width += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
height += child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
}
}
setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, 0),
resolveSizeAndState(height, heightMeasureSpec, 0));
}
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childLeft = 0;
int childTop = 0;
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
child.layout(childLeft + lp.leftMargin, childTop + lp.topMargin,
childLeft + childWidth + lp.rightMargin,
childTop + childHeight + lp.bottomMargin);
childLeft += childWidth + lp.leftMargin + lp.rightMargin;
childTop += childHeight + lp.topMargin + lp.bottomMargin;
}
}
}
protected void onDraw(Canvas canvas) {
// 遍历子View,对View进行绘制
super.onDraw(canvas);
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
drawChild(canvas, child, getDrawingTime());
}
}
}
在该源码中,onMeasure()
方法主要实现了对View的测量,onLayout()
方法主要实现了对View的布局,onDraw()
方法主要实现了对View的绘制。
3. 举例说明
3.1 自定义ViewGroup的示例
以下是一个自定义LinearLayout的示例,可以通过继承ViewGroup类实现:
public class CustomLinearLayout extends ViewGroup {
public CustomLinearLayout(Context context) {
super(context);
}
public CustomLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childLeft = 0;
int childTop = 0;
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
child.layout(childLeft + lp.leftMargin, childTop + lp.topMargin,
childLeft + childWidth + lp.rightMargin,
childTop + childHeight + lp.bottomMargin);
childLeft += childWidth + lp.leftMargin + lp.rightMargin;
childTop += childHeight + lp.topMargin + lp.bottomMargin;
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
int maxChildHeight = 0;
int totalWidth = 0;
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
totalWidth += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
}
}
setMeasuredDimension(
View.resolveSize(totalWidth, widthMeasureSpec),
View.resolveSize(maxChildHeight, heightMeasureSpec)
);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new MarginLayoutParams(p);
}
}
该自定义LinearLayout继承了ViewGroup类,并重写了onLayout()
方法和onMeasure()
方法。onMeasure()
方法的作用是先通过measureChildren()方法测量所有子View的大小,然后确定自身的大小。而onLayout()
方法,则是实现对所有子View进行布局,这里实现的方式是按照水平方向排列,每个子View之间保留一定的Margin。
3.2 布局文件的示例
以下是一个简单的布局文件的示例,可以对TextView进行布局:
<com.example.CustomLinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Username:" />
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:inputType="textEmailAddress"
android:layout_marginLeft="10dp" />
</com.example.CustomLinearLayout>
这个布局文件使用了自定义的CustomLinearLayout作为根ViewGroup,该CustomLinearLayout的方向是水平的,因此它的子View(一个TextView和一个EditText)会按照水平方向进行排列。其中,TextView距下一个View有10dp的Margin。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Android布局加载流程源码 - Python技术站