Android 自定义ViewGroup实践—让某些子View优先显示完整(压缩其它View)
效果图
效果分析
- 子View横向排列
- 子View竖直居中
- 标记为显示完整的View能尽可能地完整显示
实现方式
测量--重写onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法
- 由于有优先完整显示的子View,所以第一次遍历所有子View找到需要优先显示的子View进行测量,直接调用ViewGoup中的measureChildWithMargins获取测量宽高,如下:
int widthUsed = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
LayoutParams params = (LayoutParams) child.getLayoutParams();
widthUsed += params.leftMargin;
widthUsed += params.rightMargin;
//如果是固定宽度的布局
if (params.complete) {
measureChildWithMargins(child, widthMeasureSpec, widthUsed, heightMeasureSpec, 0);
widthUsed += child.getMeasuredWidth();
}
}
- 再次遍历测量未优先显示的子View,以剩余宽高来作为最大宽高来测量,具体代码如下:
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
LayoutParams params = (LayoutParams) child.getLayoutParams();
widthUsed += params.leftMargin;
widthUsed += params.rightMargin;
if (!params.complete) {
measureChildWithMargins(child, widthMeasureSpec, widthUsed, heightMeasureSpec, 0);
}
width += widthUsed;
width += child.getMeasuredWidth();
height = Math.max(height, params.topMargin + params.bottomMargin + child.getMeasuredHeight());
}
- 经过以上的子View测量,我们能够很容易得到该ViewGroup需要的宽高,即上述中的width和height,调用setMeasuredDimension()方法设置ViewGroup的宽高。
setMeasuredDimension(getDefaultSize(width, widthMeasureSpec), getDefaultSize(height, heightMeasureSpec));
布局,重写onLayout(boolean changed, int l, int t, int r, int b)方法
- 没啥说的,唯一需要注意的是child.layout()传入的是相对与改父View的相对坐标,直接遍历所有子View,调用子View的onLayout(int left,int top,int right,int bottom)方法,为了让margin生效,需要加上margin,代码如下:
int left = 0;
final int layoutHeight = b - t;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
//获取测量宽度
final int width = child.getMeasuredWidth();
Log.d(TAG, "onLayout: " + width);
//获取测量高度
final int height = child.getMeasuredHeght();
//计算居中的上下偏移量
final int offset = (layoutHeight - height) / 2;
MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
//子View的左侧位置为left+左边距
left += params.leftMargin;
child.layout(left, offset, left + width, height + offset);
Log.d(TAG, "onLayout: [left:" + (l + left) + " top:" + (t + yOffset) + " right:" + (l + left + width) + " bottom:" + (b - yOffset));
//下一个View的左侧位置
left += width + params.rightMargin;
}
使用方式,在需要尽可能显示完整的View中添加app:complete = "ture",即可
<com.summer.myapplication.widget.NoneCompressLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:singleLine="true"
android:ellipsize="end" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="固定内容"
app:complete="true"
android:layout_marginEnd="10dp"
android:layout_marginStart="16dp" />
</com.summer.myapplication.widget.NoneCompressLayout>
详细代码