最后
下面是辛苦给大家整理的学习路线
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
-
attrs.xml文件declare-styleable标签定义及相关属性
-
在布局文件中导入自定义的属性集。两种方法
xmlns:custom=“http://schemas.android.com/apk/res/com.example”
xmlns:custom=“http://schemas.android.com/apk/res-auto”
- 代码中如何获取自定义属性值
TypedArray arry = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
float size = arry.getDimension(R.styleable.CustomView_size,10f);
// to use
ViewGroup绘制流程
View和ViewGroup绘制流程基本相同,只是ViewGroup除了绘制自己还需要绘制子控件。绘制流程分为测量 ——布局——绘制 主要对应下面三个函数:
-
onMeasure():测量当前控件大小并为布局提供建议
-
onLayout():使用layout()函数对所有子控件进行布局
-
onDraw():根据测量布局的位置绘图
measure流程和MeasureSpec
MeasureSpec是int型数字,但它由两部分组成mode+size,它转换为二进制前两位代表模式后30位代表数值,它有三种模式。MeasureSpec是View的内部类,作用是在Measure的过程中,将View的LayoutParams根据父容器所分发的规则转换成对应的MeasureSpec,最后在onMeasure根据该值确定View的宽高。
-
UNSPECIFIED: 未指定模式,子View不受父View的限制,子View可以设置任意大小。一般用于系统内部的测量。
-
EXACTLY:精确模式,对应于match_parent和具体数值,子元素被限定于给定的边界。
-
AT_MOST:最大模式,对应于wrap_content,父控件给子控件分配的SpecSize。
作为顶层的View,它没有父容器。DecorView的getRootMeasureSpec方法第一个参数windowSize是指窗口尺寸,它的MeasureSpec由自身的LayoutParams和窗口尺寸大小决定。
特别需要注意的是,wrap_content对应AT_MOST,当布局文件中配置为EXACTLY模式时,我们就直接使用该值即可,当模式为AT_MOST,我们还需要将大小设置为我们计算的值,该值应该是包含控件最大值。
View的measure流程 : 先判断有无背景,取mMinWidth和背景的最小宽度的最大值; 再通过measureSpec获取默认大小
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;
}
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
ViewGroup源码中无onMeasure方法,它的measure流程 :直接遍历测量子View的MeasureSpec,measureChild中则是先获取自己的LayoutParams ,再计算自己的getChildMeasureSpec。
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layout流程
View中的layout是用来确定自身的位置。调用层级是layout调用setFrame确定该View在父容器中的位置,最后才调用onLayout。
ViewGroup的layout则是用来确定子元素的位置,不通的布局有不同的摆放规则,但都离不开最终调用setChirdFrame方法,调用子View的layout方法确定子View的位置。为了满足多种需求,我们有时还需要获取子View的MarginLayoutParams和重写generateLayoutParams提取Margin值。
draw流程
-
如果需要绘制背景
-
保存当前canvas层
-
绘制View的内容,即onDraw()方法 是一个空实现由我们自己实现
-
绘制子View,调用dispatchDraw()对子View遍历,子View绘制
-
如果需要绘制View的褪色边缘,类似于阴影效果
-
绘制装饰,如滚动条。onDrawForeground()方法
注:getMeasuredWidth()和getWidth()函数的区别
他们大多时候是相同的,但含义是不一样的。getMeasuredWidth一般被调用在layout中,getWidth则被调用在onDraw中。我们时常会在onDraw混用两个方法,切记用错。
最后送福利了,现在关注我可以获取包含源码解析,自定义View,动画实现,架构分享等。
内容难度适中,篇幅精炼,每天只需花上十几分钟阅读即可。
大家可以跟我一起探讨,有flutter—底层开发—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!