自定義表格式布局FormLayout
項(xiàng)目中有這樣的表格式布局,如下圖
如果用LinearLayout,RelativeLayout也能實(shí)現(xiàn)這樣的布局,但是比較麻煩阵苇,布局的層級(jí)也會(huì)比較多。所以就自己自定義了一個(gè)FormLayout來(lái)展示這些信息感论。
/**
* 自定義表格布局
* <p/>
* author yyw
* date 2017/7/12
* version 1.0
* desc
*/
public class FormLayout extends ViewGroup {
private float[] mChildWeight;//表格的每列的權(quán)重绅项,用來(lái)計(jì)算每列表格的寬度,如果為空表示平均分配
private int columnCount;//表格的總列數(shù)
private int rowCount;//表格的總行數(shù)
private int dividerSize = 2;//表格邊框的寬度
private int[] mChildColumnWidth;//表格每列的寬度
private int[] mChildRowHeight;//表格每行的高度
private Path mDividerPath;//邊框的路徑
private Paint mDividerPaint;//畫筆
public FormLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.FormLayout);
CharSequence[] weightArray = array.getTextArray(R.styleable.FormLayout_column_weight_array);
setWeight(weightArray);
columnCount = array.getInt(R.styleable.FormLayout_columnCount, 0);
rowCount = array.getInt(R.styleable.FormLayout_rowCount, 0);
int mDividerColor = array.getColor(R.styleable.FormLayout_dividerColor, Color.BLACK);
dividerSize = array.getDimensionPixelSize(R.styleable.FormLayout_dividerWidth, dividerSize);
array.recycle();
mChildColumnWidth = new int[columnCount];
mChildRowHeight = new int[rowCount];
mDividerPath = new Path();
mDividerPaint = new Paint();
mDividerPaint.setColor(mDividerColor);
mDividerPaint.setAntiAlias(true);
mDividerPaint.setStrokeWidth(dividerSize);
mDividerPaint.setStyle(Paint.Style.STROKE);
setWillNotDraw(false);
}
private void setWeight(CharSequence[] weightArray) {
if (weightArray == null) return;
mChildWeight = new float[weightArray.length];
for (int i = 0; i < weightArray.length; i++) {
float w = Float.parseFloat(weightArray[i].toString().trim());
mChildWeight[i] = w;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (columnCount == 0 || rowCount == 0) return;
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
//計(jì)算每列的寬度
fillChildWidth(widthSize);
for (int i = 0; i < mChildRowHeight.length; i++) {
//計(jì)算每行的高度
mChildRowHeight[i] = getChildAtRowMaxHeight(i);
}
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
LayoutParams params = (LayoutParams) child.getLayoutParams();
//計(jì)算總的寬度
int childWidth = 0;
for (int j = params.columnIndex; j < (params.columnCount + params.columnIndex); j++) {
childWidth += mChildColumnWidth[j];
}
childWidth += (params.columnCount - 1) * dividerSize;
//計(jì)算總的高度
int childHeight = 0;
for (int j = params.rowIndex; j < (params.rowIndex + params.rowCount); j++) {
childHeight += mChildRowHeight[j];
}
childHeight += (params.rowCount - 1) * dividerSize;
int childWidthMeasureSpec = getChildMeasureSpec(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
getPaddingLeft() + getPaddingRight() + params.leftMargin
+ params.rightMargin, childWidth);
int childHeightMeasureSpec = getChildMeasureSpec(MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY),
getPaddingTop() + getPaddingBottom() + params.topMargin
+ params.bottomMargin, childHeight);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
//計(jì)算布局的總高度
int allHeight = 0;
for (int h : mChildRowHeight) {
allHeight += h;
}
allHeight += (rowCount + 1) * dividerSize;
allHeight += getPaddingBottom() + getPaddingTop();
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(allHeight, MeasureSpec.EXACTLY));
}
/**
* 獲取每行的最大的高度
*
* @param row 行數(shù)
* @return 最大高度
*/
private int getChildAtRowMaxHeight(int row) {
final int childCount = getChildCount();
int maxHeight = 0;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
LayoutParams params = (LayoutParams) child.getLayoutParams();
if (row >= params.rowIndex && row < (params.rowIndex + params.rowCount)) {//表示在獲取范圍內(nèi)
int childWidth = 0;
for (int j = params.columnIndex; j < (params.columnCount + params.columnIndex); j++) {
childWidth += mChildColumnWidth[j];//計(jì)算所占列的總寬度
}
childWidth += (params.columnCount - 1) * dividerSize;//計(jì)算總的寬度
int childHeight = getChildMeasureHeight(child, params, childWidth);
int rowHeight = (childHeight - (params.rowCount - 1) * dividerSize) / params.rowCount;//把寬度分給所占行的高度
maxHeight = Math.max(maxHeight, rowHeight);
}
}
return maxHeight;
}
/**
* 獲取行高
*
* @param child 子view
* @param lp 子view的LayoutParams
* @param childWidth 子view的寬度
* @return 獲取子view的高度
*/
private int getChildMeasureHeight(View child, LayoutParams lp, int childWidth) {
int childWidthMeasureSpec = getChildMeasureSpec(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
getPaddingLeft() + getPaddingRight() + lp.leftMargin
+ lp.rightMargin, childWidth);
int childHeightMeasureSpec = getChildMeasureSpec(MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.UNSPECIFIED),
getPaddingTop() + getPaddingBottom() + lp.topMargin
+ lp.bottomMargin, ViewGroup.LayoutParams.WRAP_CONTENT);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
return child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
}
/**
* 計(jì)算每列的位置
*
* @param widthSize 寬度
*/
private void fillChildWidth(int widthSize) {
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
widthSize = widthSize - paddingLeft - paddingRight - (columnCount + 1) * dividerSize;
if (mChildWeight != null && mChildWeight.length == mChildColumnWidth.length) {
float allWeight = 0.0f;
for (float w : mChildWeight) {
allWeight += w;
}
float weightStep = widthSize / allWeight;
for (int i = 0; i < mChildWeight.length; i++) {
mChildColumnWidth[i] = Math.round(mChildWeight[i] * weightStep);
}
} else {
float weightStep = widthSize / mChildColumnWidth.length;
for (int i = 0; i < mChildColumnWidth.length; i++) {
mChildColumnWidth[i] = Math.round(weightStep);
}
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int childTop = getPaddingTop() + dividerSize;
final int childLeft = getPaddingLeft() + dividerSize;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams params = (LayoutParams) child.getLayoutParams();
int t = getCurrentTop(childTop, params.rowIndex) + params.rowIndex * dividerSize;
int l = getCurrentLeft(childLeft, params.columnIndex) + params.columnIndex * dividerSize;
int b = t + child.getMeasuredHeight();
int r = l + child.getMeasuredWidth();
child.layout(l, t, r, b);
}
}
private int getCurrentTop(int childTop, int rowIndex) {
for (int i = 0; i < rowIndex; i++) {
childTop += mChildRowHeight[i];
}
return childTop;
}
private int getCurrentLeft(int childLeft, int columnIndex) {
for (int i = 0; i < columnIndex; i++) {
childLeft += mChildColumnWidth[i];
}
return childLeft;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//畫邊框
mDividerPath.reset();
final int childCount = getChildCount();
final int halfSize = dividerSize / 2;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
mDividerPath.moveTo(child.getLeft() - halfSize, child.getTop() - halfSize);
mDividerPath.lineTo(child.getLeft() - halfSize, child.getBottom() + halfSize);
mDividerPath.lineTo(child.getRight() + halfSize, child.getBottom() + halfSize);
mDividerPath.lineTo(child.getRight() + halfSize, child.getTop() - halfSize);
mDividerPath.lineTo(child.getLeft() - halfSize, child.getTop() - halfSize);
}
canvas.drawPath(mDividerPath, mDividerPaint);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
/**
* Per child parameters for children views of the {@link FormLayout}.
*/
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
public int rowIndex;
public int rowCount;
public int columnIndex;
public int columnCount;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray array = c.obtainStyledAttributes(attrs, R.styleable.FormLayout_Layout);
rowIndex = array.getInt(R.styleable.FormLayout_Layout_layout_rowIndex, 0);
rowCount = array.getInt(R.styleable.FormLayout_Layout_layout_rowCount, 1);
columnIndex = array.getInt(R.styleable.FormLayout_Layout_layout_columnIndex, 0);
columnCount = array.getInt(R.styleable.FormLayout_Layout_layout_columnCount, 1);
array.recycle();
}
public LayoutParams(@Px int width, @Px int height) {
super(new ViewGroup.LayoutParams(width, height));
}
public LayoutParams(LayoutParams source) {
super(source);
rowIndex = source.rowIndex;
rowCount = source.rowCount;
columnIndex = source.columnIndex;
columnCount = source.columnCount;
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
}
自定義屬性
<resources>
<declare-styleable name="FormLayout">
<attr name="column_weight_array" format="reference"/>
<attr name="rowCount" format="integer"/>
<attr name="columnCount" format="integer"/>
<attr name="dividerColor" format="color"/>
<attr name="dividerWidth" format="dimension"/>
</declare-styleable>
<declare-styleable name="FormLayout_Layout">
<attr name="layout_rowIndex" format="integer"/>
<attr name="layout_rowCount" format="integer"/>
<attr name="layout_columnIndex" format="integer"/>
<attr name="layout_columnCount" format="integer"/>
</declare-styleable>
</resources>
Demo
代碼布局:
<com.tongyan.qrcode.support.widget.FormLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:columnCount="3"
app:rowCount="3">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="占一列三行"
app:layout_columnIndex="0"
app:layout_rowCount="3"
app:layout_rowIndex="0"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="占兩列一行"
app:layout_columnCount="2"
app:layout_columnIndex="1"
app:layout_rowIndex="0"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="占一列一行"
app:layout_columnIndex="1"
app:layout_rowIndex="1"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="占一列一行"
app:layout_columnIndex="1"
app:layout_rowIndex="2"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="占一列兩行"
app:layout_columnIndex="2"
app:layout_rowCount="2"
app:layout_rowIndex="1"/>
</com.tongyan.qrcode.support.widget.FormLayout>
如果要改動(dòng)每列的寬度權(quán)重
可以定義一個(gè)String數(shù)組
<string-array name="demo">
<item>1</item>
<item>1.5</item>
<item>2</item>
</string-array>
在布局中加入
app:column_weight_array="@array/demo"
效果圖