自定义控件测量
自定义 View
测量
自定义 Layout
测量
第一种情况 MeasureSpec.AT_MOST
第二种情况 MeasureSpec.EXACTLY
第三种情况 MeasureSpec.UNSPECIFIED
NestedScrollView
测量子 View 高度时
当 MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED 时, MeasureSpec.getSize(widthMeasureSpec) 获取到的尺寸都是最大尺寸, 就算设置了相关 layout 属性还是一样. 为了避免这种情况可以通过 LayoutParams 获取 layout 属性, 根据实际情况去测量计算
// ShadowLayout 部分代码
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (getChildCount() < 1) {
return;
}
// ======================================================
// 通过 MeasureSpec.getMode 获取模式存在一个问题:
// 当获取 MeasureSpec.getMode == MeasureSpec.UNSPECIFIED 时,
// 即使 xml 宽高设置的是具体值, MeasureSpec.getSize 获取到也还是推荐的值.
// ======================================================
// 下面的代码就是为了解决这种情况:
// 为了避免 MeasureSpec.UNSPECIFIED 模式下获取不到 xml 设置的具体宽高值, 直接从 LayoutParams 中获取
int width = getLayoutParams().width;
int height = getLayoutParams().height;
// 计算剩余空间
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int useWidth = (width > 0 ? width : widthSize) - mLeftPadding - mRightPadding;
int childWidthSpec = MeasureSpec.makeMeasureSpec(useWidth, MeasureSpec.EXACTLY);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int useHeight = (height > 0 ? height : heightSize) - mTopPadding - mBottomPadding;
int childHeightSpec = MeasureSpec.makeMeasureSpec(useHeight, MeasureSpec.EXACTLY);
View child = getChildAt(0);
measureChild(child, childWidthSpec, childHeightSpec);
// MATCH_PARENT
if (width == ViewGroup.LayoutParams.MATCH_PARENT) {
width = widthSize;
}
if (height == ViewGroup.LayoutParams.MATCH_PARENT) {
height = heightSize;
}
// WRAP_CONTENT
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
if (width == ViewGroup.LayoutParams.WRAP_CONTENT) {
width = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin + mLeftPadding + mRightPadding;
}
if (height == ViewGroup.LayoutParams.WRAP_CONTENT) {
height = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin + mTopPadding + mBottomPadding;
}
setMeasuredDimension(width, height);
}
自定义 layout
测量踩坑记录
java.lang.ClassCastException: android.view.ViewGroup$LayoutParams cannot be cast to android.view.ViewGroup$MarginLayoutParams
如果自定义 layout
是直接继承的 ViewGroup
, childView
的默认 LayoutParams 就是 ViewGroup.LayoutParams
如果想要转换成 MarginLayoutParams
需要重写如下方法:
override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
return MarginLayoutParams(context, attrs)
}
override fun generateLayoutParams(lp: LayoutParams?): LayoutParams {
return MarginLayoutParams(lp)
}
override fun generateDefaultLayoutParams(): LayoutParams {
return MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
}
参考链接
踩坑记录
自定义 Layout
中, findViewById
获得的 View
为 null
原因: 大概因为布局未加载完成
解决方案:
override fun onFinishInflate() {
// 将 findViewById 相关的逻辑挪到此处即可
}