概要
JList
を垂直方向ニュースペーパー・スタイルレイアウトに設定して各セルにLED
風Icon
を配置し、ドットマトリクスLED
デジタル時計を作成します。
Screenshot
Advertisement
サンプルコード
private static JList<Boolean> makeLedDotMatrixList(
ListModel<Boolean> model, Dimension dim) {
return new JList<Boolean>(model) {
@Override public void updateUI() {
setFixedCellWidth(dim.width);
setFixedCellHeight(dim.height);
setVisibleRowCount(ROW);
setCellRenderer(null);
super.updateUI();
setLayoutOrientation(JList.VERTICAL_WRAP);
setFocusable(false);
ListCellRenderer<? super Boolean> renderer = getCellRenderer();
Icon on = new LedDotIcon(true, dim);
Icon off = new LedDotIcon(false, dim);
setCellRenderer((list, value, index, isSelected, cellHasFocus) -> {
Component c = renderer.getListCellRendererComponent(
list, null, index, false, false);
if (c instanceof JLabel) {
((JLabel) c).setIcon(Objects.equals(Boolean.TRUE, value) ? on : off);
}
return c;
});
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
setBackground(Color.BLACK);
}
};
}
View in GitHub: Java, Kotlin解説
JList<Boolean>
を作成し垂直方向ニュースペーパー・スタイルレイアウトに設定- セル選択などは不可に設定
- セルの値が
true
の場合はLED
風ドットIcon
を表示するListCellRenderer
を設定SynthLookAndFeel
系(Nimbus
,GTK
)のLookAndFeel
を適用した垂直方向ニュースペーパー・スタイルJList
の場合、リストセルレンダラーに描画するアイコンのx
座標がおかしくなる?
class LedDotIcon implements Icon {
private final Color on = new Color(0x32_FF_AA);
private final boolean led;
private final Dimension dim;
protected LedDotIcon(boolean led, Dimension dim) {
this.led = led;
this.dim = dim;
}
@Override public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// JList#setLayoutOrientation(VERTICAL_WRAP) + Synth, Nimbus, GTK bug???
// g2.translate(x, y);
g2.setPaint(led ? on : c.getBackground());
g2.fillOval(0, 0, getIconWidth() - 1, getIconHeight() - 1);
g2.dispose();
}
@Override public int getIconWidth() {
return dim.width;
}
@Override public int getIconHeight() {
return dim.height;
}
}
- リストモデルは
DefaultListModel<Boolean>
を使用- ひとつの数字を表示するため列
4
、行7
の28
セルを使用するので時2
、分2
の4
文字と区切りと余白の5
列分の総セル数をDefaultListModel#setSize((COLUMN * 4 + 5) * ROW)
で設定 - 秒表示用のモデルは
DefaultListModel#setSize((COLUMN * 2 + 1) * ROW)
を設定 - どちらも
DefaultListModel#getElementAt(int index)
をオーバーライドしてセルが点灯しているか判断してBoolean
を返す
- ひとつの数字を表示するため列
DefaultListModel<Boolean> model1 = new DefaultListModel<Boolean>() {
@Override public Boolean getElementAt(int index) {
return getHoursMinutesDotMatrix(time, index);
}
};
model1.setSize((COLUMN * 4 + 5) * ROW);
private static boolean getHoursMinutesDotMatrix(LocalTime time, int index) {
int ten = 10;
int hours = time.getHour();
int h1 = hours / ten;
int start = 0;
int end = start + COLUMN;
if (contains(index, start, end, h1)) {
return hours >= ten;
}
int gap = 1;
int h2 = hours - h1 * ten;
start = end + gap;
end = start + COLUMN;
if (contains(index, start, end, h2)) {
return true;
}
int seconds = time.getSecond();
int s1 = seconds / ten;
int s2 = seconds - s1 * ten;
start = end + gap;
end = start + gap;
if (index < end * ROW && s2 % 2 == 0 && DOT.contains(index - start * ROW)) {
return true;
}
int minutes = time.getMinute();
int m1 = minutes / ten;
start = end + gap;
end = start + COLUMN;
if (contains(index, start, end, m1)) {
return true;
}
int m2 = minutes - m1 * ten;
start = end + gap;
end = start + COLUMN;
return contains(index, start, end, m2);
}
Set<Integer>
を使用して数字に対してどのセルが点灯しているかを定義- 垂直方向ニュースペーパー・スタイルレイアウトなので左上から順にセル番号
0
、右下のセル番号27
までを使用して以下のように数字を定義する - 例えば数字
2
はSet.of(0, 3, 4, 5, 6, 7, 10, 13, 14, 17, 20, 21, 22, 23, 24, 27)
でどのセルを点灯するかを表現する Java 8
ではSet.of(...)
が使用できないので以下のようなimmutableSetOf()
メソッドを作成して代用しているCollections.unmodifiableSet(new HashSet<>(Arrays.asList(...)))
- 垂直方向ニュースペーパー・スタイルレイアウトなので左上から順にセル番号
private static final int COLUMN = 4;
private static final int ROW = 7;
private static final List<Set<Integer>> NUMBERS = Arrays.asList(
Set.of(0, 1, 2, 3, 4, 5, 6, 7, 13, 14, 20, 21, 22, 23, 24, 25, 26, 27), // 0
Set.of(21, 22, 23, 24, 25, 26, 27), // 1
Set.of(0, 3, 4, 5, 6, 7, 10, 13, 14, 17, 20, 21, 22, 23, 24, 27), // 2
Set.of(0, 3, 6, 7, 10, 13, 14, 17, 20, 21, 22, 23, 24, 25, 26, 27), // 3
Set.of(0, 1, 2, 3, 10, 17, 21, 22, 23, 24, 25, 26, 27), // 4
Set.of(0, 1, 2, 3, 6, 7, 10, 13, 14, 17, 20, 21, 24, 25, 26, 27), // 5
Set.of(0, 1, 2, 3, 4, 5, 6, 7, 10, 13, 14, 17, 20, 21, 24, 25, 26, 27), // 6
Set.of(0, 1, 2, 3, 7, 14, 21, 22, 23, 24, 25, 26, 27), // 7
Set.of(0, 1, 2, 3, 4, 5, 6, 7, 10, 13, 14, 17, 20, 21, 22, 23, 24, 25, 26, 27), // 8
Set.of(0, 1, 2, 3, 6, 7, 10, 13, 14, 17, 20, 21, 22, 23, 24, 25, 26, 27)); // 9
private static final List<Integer> DOT = Arrays.asList(2, 4);
- 数字の列数やドットパターンを変更
private static final int COLUMN = 5;
private static final int ROW = 7;
private static final List<Set<Integer>> NUMBERS = Arrays.asList(
Set.of(1, 2, 3, 4, 5, 7, 9, 13, 14, 17, 20, 21, 25, 27, 29, 30, 31, 32, 33), // 0
Set.of(8, 13, 14, 15, 16, 17, 18, 19, 20, 27), // 1
Set.of(1, 6, 7, 12, 13, 14, 18, 20, 21, 24, 27, 29, 30, 34), // 2
Set.of(0, 5, 7, 13, 14, 17, 20, 21, 23, 24, 27, 28, 29, 32, 33), // 3
Set.of(3, 4, 9, 11, 15, 18, 21, 22, 23, 24, 25, 26, 27, 32), // 4
Set.of(0, 1, 2, 5, 7, 9, 13, 14, 16, 20, 21, 23, 27, 28, 31, 32, 33), // 5
Set.of(1, 2, 3, 4, 5, 7, 10, 13, 14, 17, 20, 21, 24, 27, 29, 32, 33), // 6
Set.of(0, 7, 11, 12, 13, 14, 17, 21, 23, 28, 29), // 7
Set.of(1, 2, 4, 5, 7, 10, 13, 14, 17, 20, 21, 24, 27, 29, 30, 32, 33), // 8
Set.of(1, 2, 5, 7, 10, 13, 14, 17, 20, 21, 24, 27, 29, 30, 31, 32, 33)); // 9