Swing/RoundedSelectionListCalendar の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/RoundedSelectionListCalendar へ行く。
- Swing/RoundedSelectionListCalendar の差分を削除
--- category: swing folder: RoundedSelectionListCalendar title: JListで作成したカレンダーのセル選択領域を角丸で描画する tags: [JList, Path2D, Graphics, Calendar] author: aterai pubdate: 2024-04-15T05:49:23+09:00 description: JListで作成したカレンダーでセルを複数選択した領域のすべての角を丸めて描画します。 image: https://drive.google.com/uc?id=1Mmv2sjRe4isN64Nwl_0FyI9ZRedoCJMX hreflang: href: https://java-swing-tips.blogspot.com/2024/04/rounding-corners-of-rectilinear-polygon.html lang: en --- * 概要 [#summary] `JList`で作成したカレンダーでセルを複数選択した領域のすべての角を丸めて描画します。 #download(https://drive.google.com/uc?id=1Mmv2sjRe4isN64Nwl_0FyI9ZRedoCJMX) * サンプルコード [#sourcecode] #code(link){{ /** Rounding the corners of a Rectilinear Polygon. */ public static Path2D convertRoundedPath(List<Point2D> list, double arc) { double kappa = 4d * (Math.sqrt(2d) - 1d) / 3d; // = 0.55228...; double akv = arc - arc * kappa; int sz = list.size(); Point2D pt0 = list.get(0); Path2D path = new Path2D.Double(); path.moveTo(pt0.getX() + arc, pt0.getY()); for (int i = 0; i < sz; i++) { Point2D prv = list.get((i - 1 + sz) % sz); Point2D cur = list.get(i); Point2D nxt = list.get((i + 1) % sz); double dx0 = Math.signum(cur.getX() - prv.getX()); double dy0 = Math.signum(cur.getY() - prv.getY()); double dx1 = Math.signum(nxt.getX() - cur.getX()); double dy1 = Math.signum(nxt.getY() - cur.getY()); path.curveTo( cur.getX() - dx0 * akv, cur.getY() - dy0 * akv, cur.getX() + dx1 * akv, cur.getY() + dy1 * akv, cur.getX() + dx1 * arc, cur.getY() + dy1 * arc); path.lineTo(nxt.getX() - dx1 * arc, nxt.getY() - dy1 * arc); } path.closePath(); return path; } }} * 解説 [#explanation] - [[JListで月のカーソルキー移動や、週を跨いた日付を範囲選択が可能なカレンダーを作成する>Swing/CalendarViewList]]の`JList`を使用 - セルの選択状態、フォーカス状態に関係なく背景色を描画しないセルレンダラーを設定し、代わりに`JList#paintComponent(...)`をオーバーライドしてセルの選択状態を描画 - `ListSelectionModel.SINGLE_INTERVAL_SELECTION`で`1`つの連続区間のみを選択可能に設定 -- `JList#getCellBounds(...)`で選択セル領域を取得し、`Area#add(new Area(Rectangle))`でひとつ、またはふたつの`Area`にまとめる --- セルが水平方向の次に垂直方向の順で並ぶ「ニュースペーパー・スタイル」レイアウトなのでセル選択の連続区間は`1`つでもセル選択領域は`2`つになる場合がある --- セルが水平方向の次に垂直方向の順で並ぶ「ニュースペーパー・スタイル」レイアウトなのでセル選択の連続区間は`1`つでもセル選択領域は`2`つの`Area`になる場合がある -- まとめられた選択領域の各`Area`は直角多角形(任意の内角が`90°`または`270°`で辺が直角に交わる)になるので、これらの角(`Point2D`)をすべて`Path2D.curveTo(...)`で丸めた`Path2D`に変換 #code{{ public final JList<LocalDate> monthList = new JList<LocalDate>() { @Override public void updateUI() { setCellRenderer(null); super.updateUI(); setLayoutOrientation(HORIZONTAL_WRAP); setVisibleRowCount(CalendarViewListModel.ROW_COUNT); setFixedCellWidth(size.width); setFixedCellHeight(size.height); setCellRenderer(new CalendarListRenderer()); setOpaque(false); getSelectionModel().setSelectionMode( ListSelectionModel.SINGLE_INTERVAL_SELECTION); addListSelectionListener(e -> repaint()); } @Override protected void paintComponent(Graphics g) { int[] indices = getSelectedIndices(); if (indices != null && indices.length > 0) { Graphics2D g2 = (Graphics2D) g.create(); g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setPaint(SELECTED_COLOR); Area area = new Area(); Arrays.stream(indices) .mapToObj(i -> getCellBounds(i, i)) .forEach(r -> area.add(new Area(r))); for (Area a : GeomUtils.singularization(area)) { List<Point2D> lst = GeomUtils.convertAreaToPoint2DList(a); g2.fill(GeomUtils.convertRoundedPath(lst, 4d)); } g2.dispose(); } super.paintComponent(g); } }; }} * 参考リンク [#reference] - [[JListで月のカーソルキー移動や、週を跨いた日付を範囲選択が可能なカレンダーを作成する>Swing/CalendarViewList]] - [[JTreeの選択領域描画をラウンド矩形に変更する>Swing/RoundedSelectionTree]] -- セルを垂直方向に`1`列に並べたレイアウト(デフォルト、`JList.VERTICAL`)の`JList`でセル選択領域の角を丸める場合は、リンク先の`JTree`の方法がそのまま利用可能 * コメント [#comment] #comment #comment