android表格布局源码,Android TableLayout源码学习

OverView

TableLayout是Android的六大布局容器之一,不过日常开发中使用得并不多,顾名思义,其主要特点是像表格一样,有行和列的概念。

重要属性

TableLayout有以下重要属性:

android:shrinkColumns属性,当TableRow里边的空间布满布局的时候,指定列自动延伸以填充可用部分。当TableRow里边的控件还没有布满布局时,android:shrinkColumns不起作用。

android:stretchColumns属性,用于指定列对空白部分进行填充

android:collapseColumns属性,用于隐藏指定的列

继承层次及构造函数

public class TableLayout extends LinearLayout

从继承层次上能够看到,TableLayout实际上是继承自LinearLayout的,直观上可以理解成,TableLayout就是一行行的TableRow线性排列而成。

public TableLayout(Context context, AttributeSet attrs) {

super(context, attrs);

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TableLayout);

String stretchedColumns = a.getString(R.styleable.TableLayout_stretchColumns);

if (stretchedColumns != null) {

if (stretchedColumns.charAt(0) == '*') {

mStretchAllColumns = true;

} else {

mStretchableColumns = parseColumns(stretchedColumns);

}

}

String shrinkedColumns = a.getString(R.styleable.TableLayout_shrinkColumns);

if (shrinkedColumns != null) {

if (shrinkedColumns.charAt(0) == '*') {

mShrinkAllColumns = true;

} else {

mShrinkableColumns = parseColumns(shrinkedColumns);

}

}

String collapsedColumns = a.getString(R.styleable.TableLayout_collapseColumns);

if (collapsedColumns != null) {

mCollapsedColumns = parseColumns(collapsedColumns);

}

a.recycle();

initTableLayout();

}

构造函数也不长,主要就是对前文提到过的几个重要属性的分析,看下里面的parseColumns的实现:

private static SparseBooleanArray parseColumns(String sequence) {

SparseBooleanArray columns = new SparseBooleanArray();

Pattern pattern = Pattern.compile("\\s*,\\s*");

String[] columnDefs = pattern.split(sequence);

for (String columnIdentifier : columnDefs) {

try {

int columnIndex = Integer.parseInt(columnIdentifier);

// only valid, i.e. positive, columns indexes are handled

if (columnIndex >= 0) {

// putting true in this sparse array indicates that the

// column index was defined in the XML file

columns.put(columnIndex, true);

}

} catch (NumberFormatException e) {

// we just ignore columns that don't exist

}

}

return columns;

}

在设置shrinkColumns时都是可以设置多个值的,值之间通过逗号分隔,如"0,2",在parseColumns中就将其中的值解析出来了。

onMeasure

接下来看下TableLayout的测量过程做了什么:

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

// enforce vertical layout

measureVertical(widthMeasureSpec, heightMeasureSpec);

}

@Override

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {

findLargestCells(widthMeasureSpec, heightMeasureSpec);

shrinkAndStretchColumns(widthMeasureSpec);

super.measureVertical(widthMeasureSpec, heightMeasureSpec);

}

TableLayout的onMeasure()过程很清晰,分别是自身两个方法findLargestCells()和shrinkAndStretchColumns()的调用,随后再调用父类LinearLayout的measureVertical()方法

先看下findLargestCells()的实现:

/**

*

Finds the largest cell in each column. For each column, the width of

* the largest cell is applied to all the other cells.

*

* @param widthMeasureSpec the measure constraint imposed by our parent

*/

private void findLargestCells(int widthMeasureSpec, int heightMeasureSpec) {

boolean firstRow = true;

// find the maximum width for each column

// the total number of columns is dynamically changed if we find

// wider rows as we go through the children

// the array is reused for each layout operation; the array can grow

// but never shrinks. Unused extra cells in the array are just ignored

// this behavior avoids to unnecessary grow the array after the first

// layout operation

final int count = getChildCount();

for (int i = 0; i < count; i++) {

final View child = getChildAt(i);

if (child.getVisibility() == GONE) {

continue;

}

if (child instanceof TableRow) {

final TableRow row = (TableRow) child;

// forces the row's height

final ViewGroup.LayoutParams layoutParams = row.getLayoutParams();

layoutParams.height = LayoutParams.WRAP_CONTENT;

final int[] widths = row.getColumnsWidths(widthMeasureSpec, heightMeasureSpec);

final int newLength = widths.length;

// this is the first row, we just need to copy the values

if (firstRow) {

if (mMaxWidths == null || mMaxWidths.length != newLength) {

mMaxWidths = new int[newLength];

}

System.arraycopy(widths, 0, mMaxWidths, 0, newLength);

firstRow = false;

} else {

int length = mMaxWidths.length;

final int difference = newLength - length;

// the current row is wider than the previous rows, so

// we just grow the array and copy the values

if (difference > 0) {

final int[] oldMaxWidths = mMaxWidths;

mMaxWidths = new int[newLength];

System.arraycopy(oldMaxWidths, 0, mMaxWidths, 0,

oldMaxWidths.length);

System.arraycopy(widths, oldMaxWidths.length,

mMaxWidths, oldMaxWidths.length, difference);

}

// the row is narrower or of the same width as the previous

// rows, so we find the maximum width for each column

// if the row is narrower than the previous ones,

// difference will be negative

final int[] maxWidths = mMaxWidths;

length = Math.min(length, newLength);

for (int j = 0; j < length; j++) {

maxWidths[j] = Math.max(maxWidths[j], widths[j]);

}

}

}

}

}

从方法前的注释可以看出,该方法的作用是,找出每一列最宽的单元格,从方法的具体实现中能看到,最终每一列的最宽宽度会存储到maxWidth这个数组中。

接下来看下为何要找到每一列的最大宽度,答案在shrinkAndStretchColumns()方法中:

private void shrinkAndStretchColumns(int widthMeasureSpec) {

// when we have no row, mMaxWidths is not initialized and the loop

// below could cause a NPE

if (mMaxWidths == null) {

return;

}

// should we honor AT_MOST, EXACTLY and UNSPECIFIED?

int totalWidth = 0;

for (int width : mMaxWidths) {

totalWidth += width;

}

int size = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight;

if ((totalWidth > size) && (mShrinkAllColumns || mShrinkableColumns.size() > 0)) {

// oops, the largest columns are wider than the row itself

// fairly redistribute the row's width among the columns

mutateColumnsWidth(mShrinkableColumns, mShrinkAllColumns, size, totalWidth);

} else if ((totalWidth < size) && (mStretchAllColumns || mStretchableColumns.size() > 0)) {

// if we have some space left, we distribute it among the

// expandable columns

mutateColumnsWidth(mStretchableColumns, mStretchAllColumns, size, totalWidth);

}

}

从这个函数中可以看到上述maxWidth的作用:

将各列的maxWidth累加得到占据的总宽度

根据总宽度和容器宽度的大小关系,分别调用mutateColumnsWidth方法对此前设置的可伸缩列进行宽度调整,以达到设置前文提到的重要属性的设置效果

onLayout

@Override

protected void onLayout(boolean changed, int l, int t, int r, int b) {

// enforce horizontal layout

layoutHorizontal(l, t, r, b);

}

在onLayout()阶段,调用的是LinearLayout中的layoutHorizontal()实现,再一次说明,TableLayout其实就是竖直方向上的LinearLayout。

总结

TableLayout继承自LinearLayout,可以看做是竖直方向的LinearLayout

TableLayout的测量阶段,主要的工作是,将各列的宽度之和和容器宽度相比,根据大小关系对列进行拉伸调整

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值