JButtonをハニカム構造でレイアウトする
Total: 199, Today: 1, Yesterday: 4
Posted by aterai at
Last-modified:
Summary
JButtonの形状を正六角形に設定し、これらを蜂の巣状(ハニカム)に配置するLayoutManagerを作成します。
Screenshot

Advertisement
Source Code Examples
// Honeycomb hexagon button layout manager
// Row pattern
// Even rows (0, 2, ...): 2n-1 buttons, offset right by half cell width
// Odd rows (1, 3, ...): 2n buttons, flush left
class HoneycombLayout implements LayoutManager {
private static final double RATIO = Math.sqrt(3d) / 2d;
private final int rows;
private final int evenCols; // Button count for even rows (2n-1)
private final int oddCols; // Button count for odd rows (2n)
// Visual gap between adjacent hexagon edges, in pixels.
// gap = 0 : edges touch perfectly
// gap > 0 : uniform spacing
private final int gap;
protected HoneycombLayout(int rows, int evenCols, int oddCols, int gap) {
this.rows = rows;
this.evenCols = evenCols;
this.oddCols = oddCols;
this.gap = gap;
}
@Override public void layoutContainer(Container parent) {
Insets insets = parent.getInsets();
int maxWidth = parent.getWidth() - insets.left - insets.right;
int maxHeight = parent.getHeight() - insets.top - insets.bottom;
Dimension buttonSize = getButtonSize(maxWidth, maxHeight);
int slotW = buttonSize.width + gap; // Horizontal pitch
int slotH = buttonSize.height + gap; // Vertical base
// Center the grid inside the panel
int gridW = oddCols * slotW;
int gridH = (int) (slotH * (.25 + .75 * rows));
int marginX = insets.left + (maxWidth - gridW) / 2;
int marginY = insets.top + (maxHeight - gridH) / 2;
int compIdx = 0;
for (int r = 0; r < rows; r++) {
boolean isEvenRow = r % 2 == 0;
int colsInRow = isEvenRow ? evenCols : oddCols;
// Y position: step by 75% of slot height
int y = marginY + (int) (r * slotH * .75 + gap / 2d);
// Even rows shift right by half a slot
int rowOffsetX = isEvenRow ? slotW / 2 : 0;
for (int col = 0; col < colsInRow; col++) {
if (compIdx >= parent.getComponentCount()) {
break;
}
Component c = parent.getComponent(compIdx);
int x = marginX + rowOffsetX + col * slotW + gap / 2;
c.setBounds(x, y, buttonSize.width, buttonSize.height);
compIdx += 1;
}
}
}
private Dimension getButtonSize(int maxWidth, int maxHeight) {
// Derive cellW,cellH from horizontal constraint
double cwFromWidth = (double) maxWidth / oddCols - gap;
double chFromWidth = cwFromWidth / RATIO;
// Derive cellW,cellH from vertical constraint
double chFromHeight = maxHeight / (.25 + .75 * rows) - gap;
double cwFromHeight = chFromHeight * RATIO;
// Adopt the smaller to satisfy both constraints
double cellW;
double cellH;
if (cwFromWidth <= cwFromHeight) {
cellW = cwFromWidth;
cellH = chFromWidth;
} else {
cellW = cwFromHeight;
cellH = chFromHeight;
}
int buttonW = Math.max(1, (int) cellW);
int buttonH = Math.max(1, (int) cellH);
return new Dimension(buttonW, buttonH);
}
@Override public Dimension preferredLayoutSize(Container parent) {
return new Dimension(500, 400);
}
@Override public Dimension minimumLayoutSize(Container parent) {
return new Dimension(200, 150);
}
@Override public void addLayoutComponent(String name, Component comp) {
// not needed
}
@Override public void removeLayoutComponent(Component comp) {
// not needed
}
}
View in GitHub: Java, KotlinDescription
- 正六角形ボタンを蜂の巣状(ハニカム)に配置する
HoneycombLayout(LayoutManagerを実装)を作成- 偶数行(
0, 2, ...)には2n-1個の正六角形ボタンを半セル分右にオフセットしながら配置 - 奇数行(
1, 3, ...)には2n個の正六角形ボタンを左揃えで配置 - 尖り上正六角形の外接円半径を
Rとすると、そのバウンディングボックス幅はW = R * sqrt(3)、バウンディングボックス高はH = R * 2 - 列のステップ幅を
slotW = W + gap、行ステップの基準をslotH = H + gapとすると、行ステップはstep = slotH * 0.75となり行は高さの3/4(75%)でかみ合う
- 偶数行(
- 尖り上正六角形
JButton- コンポーネント境界にぴったり収まる尖り上正六角形
Polygonを作成 -PI/2P(12時方向)から60°(PI/3)刻みで頂点を打つ- JButtonの形を変更
- コンポーネント境界にぴったり収まる尖り上正六角形