Swing/DashedBorderAnimationForSelectedCells の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/DashedBorderAnimationForSelectedCells へ行く。
- Swing/DashedBorderAnimationForSelectedCells の差分を削除
--- category: swing folder: DashedBorderAnimationForSelectedCells title: JTableのセル選択領域の縁に破線が移動するアニメーションを表示する tags: [JTable, Animation, Timer, BasicStroke, JLayer] author: aterai pubdate: 2025-10-20T02:10:48+09:00 description: JTableのセル選択領域全体の縁に破線フェーズの異なる破線を交互に切り替えることでその移動アニメーションを描画します。 image: https://drive.google.com/uc?id=1BXeXb29QvuCa0j_OVcBybGOY641IF_ZN --- * Summary [#summary] `JTable`のセル選択領域全体の縁に破線フェーズの異なる破線を交互に切り替えることでその移動アニメーションを描画します。 #download(https://drive.google.com/uc?id=1BXeXb29QvuCa0j_OVcBybGOY641IF_ZN) * Source Code Examples [#sourcecode] #code(link){{ class TranslucentCellSelectionLayerUI extends LayerUI<JScrollPane> { private static final float WIDTH = 2f; private static final float MITER = 5f; private static final float[] DASH = {4f, 2f}; private static final Stroke BORDER_STROKE1 = new BasicStroke( WIDTH, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, MITER, DASH, 0f); private static final Stroke BORDER_STROKE2 = new BasicStroke( WIDTH, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, MITER, DASH, 2f); private static Stroke borderStroke = BORDER_STROKE1; private JTable table; private boolean flg; private final Timer animator = new Timer(480, e -> { if (table != null && !table.isEditing()) { borderStroke = flg ? BORDER_STROKE1 : BORDER_STROKE2; repaintSelectedArea(table); flg = !flg; } }); @Override public void installUI(JComponent c) { super.installUI(c); if (c instanceof JLayer) { ((JLayer<?>) c).setLayerEventMask(AWTEvent.FOCUS_EVENT_MASK); } } @Override public void uninstallUI(JComponent c) { if (c instanceof JLayer) { ((JLayer<?>) c).setLayerEventMask(0); } super.uninstallUI(c); } @Override protected void processFocusEvent( FocusEvent e, JLayer<? extends JScrollPane> l) { table = getTable(l); if (e.getID() == FocusEvent.FOCUS_GAINED) { animator.start(); } else { animator.stop(); } super.processFocusEvent(e, l); } @Override public void paint(Graphics g, JComponent c) { super.paint(g, c); JTable table = getTable(c); int cc = table.getSelectedColumnCount(); int rc = table.getSelectedRowCount(); if (cc != 0 && rc != 0 && !table.isEditing()) { Graphics2D g2 = (Graphics2D) g.create(); g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Area area = new Area(); getSelectedArea(table).forEach(r -> { Rectangle rect = SwingUtilities.convertRectangle(table, r, c); area.add(new Area(rect)); }); Dimension ics = table.getIntercellSpacing(); Color v = table.getSelectionBackground(); Color sbc = new Color(v.getRed(), v.getGreen(), v.getBlue(), 0x32); for (Area a : GeomUtils.singularization(area)) { Rectangle r = a.getBounds(); r.width -= ics.width - 1; r.height -= ics.height - 1; g2.setPaint(sbc); g2.fill(r); g2.setPaint(v); g2.setStroke(borderStroke); g2.draw(r); } g2.dispose(); } } private static List<Rectangle> getSelectedArea(JTable tbl) { List<Rectangle> list = new ArrayList<>(); for (int row : tbl.getSelectedRows()) { for (int col : tbl.getSelectedColumns()) { if (tbl.isCellSelected(row, col)) { list.add(tbl.getCellRect(row, col, true)); } } } return list; } private static JTable getTable(Component c) { JTable table = null; if (c instanceof JLayer) { Component c1 = ((JLayer<?>) c).getView(); if (c1 instanceof JScrollPane) { table = (JTable) ((JScrollPane) c1).getViewport().getView(); } } return table; } private static void repaintSelectedArea(JTable tbl) { int cc = tbl.getSelectedColumnCount(); int rc = tbl.getSelectedRowCount(); if (cc != 0 && rc != 0) { Area area = new Area(); getSelectedArea(tbl).forEach(r -> area.add(new Area(r))); tbl.repaint(area.getBounds()); } } } }} * Description [#description] - [[JTableのセル選択を半透明化して上書き>Swing/TranslucentTableCellSelection]]と同様に`JTable`側の選択背景色は完全に透明、選択文字色は文字色と同一に設定し、セル選択領域やその縁の描画は`JLayer`を適用して`LayerUI#paint(...)`メソッド内で実行するよう設定 - [https://docs.oracle.com/javase/jp/8/docs/api/java/awt/BasicStroke.html#getDashPhase-- 破線フェーズ]のみ`2f`ずらした`BasicStroke`を`2`つ用意し、セル選択領域が存在し、かつ`JTable`にフォーカスが存在する場合は`Timer`を起動してこれらを入れ替えて描画することで破線の移動アニメーションを実行 - [https://docs.oracle.com/javase/jp/8/docs/api/java/awt/BasicStroke.html#getDashPhase-- 破線フェーズ]のみ`2f`ずらした`BasicStroke`を`2`つ用意 -- セル選択領域が存在しかつ`JTable`にフォーカスが存在する場合は`Timer`を起動してこれらを入れ替えて描画することで破線の移動アニメーションを実行 * Reference [#reference] - [[JTableのセル選択を半透明化して上書き>Swing/TranslucentTableCellSelection]] * Comment [#comment] #comment #comment