Swing/DotMatrixLedDigitalClock の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/DotMatrixLedDigitalClock へ行く。
- Swing/DotMatrixLedDigitalClock の差分を削除
--- category: swing folder: DotMatrixLedDigitalClock title: JListでドットマトリクスLEDデジタル時計を作成する tags: [JList, Icon, Timer, Clock] author: aterai pubdate: 2023-03-13T04:54:56+09:00 description: JListを垂直方向ニュースペーパー・スタイルレイアウトに設定して各セルにLED風Iconを配置し、ドットマトリクスLEDデジタル時計を作成します。 image: https://drive.google.com/uc?id=1Ml6DCmp11wZYl1r7nBmznwupB4J1MdIC hreflang: https://java-swing-tips.blogspot.com/2023/03/create-dot-matrix-led-digital-clock.html href: https://java-swing-tips.blogspot.com/2023/03/create-dot-matrix-led-digital-clock.html lang: en --- * 概要 [#summary] `JList`を垂直方向ニュースペーパー・スタイルレイアウトに設定して各セルに`LED`風`Icon`を配置し、ドットマトリクス`LED`デジタル時計を作成します。 #download(https://drive.google.com/uc?id=1Ml6DCmp11wZYl1r7nBmznwupB4J1MdIC) * サンプルコード [#sourcecode] #code(link){{ 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); } }; } }} * 解説 [#explanation] - `JList<Boolean>`を作成し垂直方向ニュースペーパー・スタイルレイアウトに設定 -- セル選択などは不可に設定 -- セルの値が`true`の場合は`LED`風ドット`Icon`を表示する`ListCellRenderer`を設定 --- `SynthLookAndFeel`系(`Nimbus`, `GTK`)の`LookAndFeel`を適用した垂直方向ニュースペーパー・スタイル`JList`の場合、リストセルレンダラーに描画するアイコンの`x`座標がおかしくなる? #code{{ 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`を返す #code{{ 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(...)))` #code{{ 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); }} - 数字の列数やドットパターンを変更 #img2(https://ateraimemo.com/swing/dotmatrixleddigitalclock/screenshot1.png) #code{{ 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 }} * 参考リンク [#reference] - [[AffineTransformを使用してPath2Dを変換し、7セグメントデジタル時計の数字を作成する>Swing/SevenSegmentDigitalClock]] - [[JListでウィークカレンダーを作成してヒートマップを表示する>Swing/CalendarHeatmapList]] * コメント [#comment] #comment #comment