GridLayout将一个容器的组件放在一个矩形网格中。容器被分成等大小的矩形,每个矩形中放置一个组件。那么GridLayout内部的处理逻辑是如何做的呢,下图是它的几个核心方法:
preferredLayoutSize
- 这个方法在布局之前就会调用来确定大小尺寸.
public Dimension preferredLayoutSize(Container parent) {
synchronized (parent.getTreeLock()) {
//获取容器四周内间距
Insets insets = parent.getInsets();
//获取组件数量
int ncomponents = parent.getComponentCount();
//行
int nrows = rows;
//列
int ncols = cols;
if (nrows > 0) {
//根据组件数计算真实需要的行数
ncols = (ncomponents + nrows - 1) / nrows;
} else {
//根据组件数计算真实需要的列数
nrows = (ncomponents + ncols - 1) / ncols;
}
//宽
int w = 0;
//高
int h = 0;
for (int i = 0; i < ncomponents; i++) {
//遍历组件
Component comp = parent.getComponent(i);
//获取预设尺寸大小
Dimension d = comp.getPreferredSize();
//记录行宽(以最宽的组件作为平均格子宽度)
if (w < d.width) {
w = d.width;
}
//记录行高(以最高的组件作为平均格子高度)
if (h < d.height) {
h = d.height;
}
}
//根据记录的宽高加上盒子内间距和组件之间的间隙计算出容器的尺寸大小
return new Dimension(insets.left + insets.right + ncols * w + (ncols - 1) * hgap,
insets.top + insets.bottom + nrows * h + (nrows - 1) * vgap);
}
}
minimumLayoutSize
- 这个方法用途是在计算布局所需的最小尺寸大小
public Dimension minimumLayoutSize(Container parent) {
synchronized (parent.getTreeLock()) {
//获取容器四周内间距
Insets insets = parent.getInsets();
//获取组件数量
int ncomponents = parent.getComponentCount();
//行
int nrows = rows;
//列
int ncols = cols;
if (nrows > 0) {
//根据组件数计算真实需要的行数
ncols = (ncomponents + nrows - 1) / nrows;
} else {
//根据组件数计算真实需要的列数
nrows = (ncomponents + ncols - 1) / ncols;
}
//宽
int w = 0;
//高
int h = 0;
for (int i = 0; i < ncomponents; i++) {
//遍历组件
Component comp = parent.getComponent(i);
//获取组件的最小尺寸
Dimension d = comp.getMinimumSize();
//记录行宽(以最宽的组件作为平均格子宽度)
if (w < d.width) {
w = d.width;
}
//记录行高(以最高的组件作为平均格子高度)
if (h < d.height) {
h = d.height;
}
}
//根据记录的宽高加上盒子内间距和组件之间的间隙计算出容器的尺寸大小
return new Dimension(insets.left + insets.right + ncols * w + (ncols - 1) * hgap,
insets.top + insets.bottom + nrows * h + (nrows - 1) * vgap);
}
}
layoutContainer
- 这个方法和Android中的onLayout方法很相似,因为它也是在父类Container也是onLayout方法中调用的。
public void layoutContainer(Container parent) {
synchronized (parent.getTreeLock()) {
//获取容器四周内间距
Insets insets = parent.getInsets();
//获取组件数量
int ncomponents = parent.getComponentCount();
//行
int nrows = rows;
//列
int ncols = cols;
//是否是LTR模式
boolean ltr = parent.getComponentOrientation().isLeftToRight();
//如果没有组件的话直接结束布局
if (ncomponents == 0) {
return;
}
if (nrows > 0) {
//根据组件数计算真实需要的行数
ncols = (ncomponents + nrows - 1) / nrows;
} else {
//根据组件数计算真实需要的列数
nrows = (ncomponents + ncols - 1) / ncols;
}
// To position components in the center we should:
// 1. get an amount of extra space within Container
// 2. incorporate half of that value to the left/top position
// Note that we use trancating division for widthOnComponent
// The reminder goes to extraWidthAvailable
// 为了将组件放置在中间我们需要做:
// 1. 从容器中获得一片额外空间
// 2. 将该值的一半合并到左上角
// extraWidthAvailable的值是对widthOnComponent使用了转移除法得到的
// 计算水平间隙总宽度
int totalGapsWidth = (ncols - 1) * hgap;
// 计算容器除去左右内间距的可用宽度
int widthWOInsets = parent.width - (insets.left + insets.right);
// 计算留给组件的宽度(可用宽度-总间隙宽度)/组件数量
int widthOnComponent = (widthWOInsets - totalGapsWidth) / ncols;
// 计算额外的可用宽度值用于居中使用(然鹅值为0 :-D)
// widthOnComponent * ncols = widthWOInsets - totalGapsWidth;
// widthWOInsets - (widthWOInsets - totalGapsWidth + totalGapsWidth) = 0
// extraWidthAvailable = 0;
int extraWidthAvailable = (widthWOInsets - (widthOnComponent * ncols + totalGapsWidth)) / 2;
// 计算垂直间隙的总高度
int totalGapsHeight = (nrows - 1) * vgap;
// 计算容器除去左右内间距的可用高度
int heightWOInsets = parent.height - (insets.top + insets.bottom);
// 计算留给组件的高度(可用高度-总间隙宽度)/组件数量
int heightOnComponent = (heightWOInsets - totalGapsHeight) / nrows;
//计算额外的可用高度值用于居中使用(然鹅值为0 :-D) 推理同extraWidthAvailable
int extraHeightAvailable = (heightWOInsets - (heightOnComponent * nrows + totalGapsHeight)) / 2;
//根据布局模式分两种情况讨论
// c代表行 r代表列 x横坐标 y纵坐标
// 每次计算相对应的x和y坐标值
// LTR: 第一个组件需要加上左上内间距值(x = insets.left + extraWidthAvailable, y = insets.top + extraHeightAvailable)
// RTL: 第一个组件需要加上上内间距减去右内间距(x = (parent.width - insets.right - widthOnComponent),insets.top + extraHeightAvailable)
if (ltr) {
for (int c = 0, x = insets.left + extraWidthAvailable; c < ncols; c++, x += widthOnComponent + hgap) {
for (int r = 0, y = insets.top + extraHeightAvailable; r < nrows; r++, y += heightOnComponent + vgap) {
int i = r * ncols + c;
if (i < ncomponents) {
parent.getComponent(i).setBounds(x, y, widthOnComponent, heightOnComponent);
}
}
}
} else {
for (int c = 0, x = (parent.width - insets.right - widthOnComponent) - extraWidthAvailable; c < ncols; c++, x -= widthOnComponent + hgap) {
for (int r = 0, y = insets.top + extraHeightAvailable; r < nrows; r++, y += heightOnComponent + vgap) {
int i = r * ncols + c;
if (i < ncomponents) {
parent.getComponent(i).setBounds(x, y, widthOnComponent, heightOnComponent);
}
}
}
}
}
}