Swing/SemicircleGauge の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/SemicircleGauge へ行く。
- Swing/SemicircleGauge の差分を削除
--- category: swing folder: SemicircleGauge title: JProgressBarの形状をドーナツ状の半円に変更してスピードメーターを作成する tags: [JProgressBar, SwingWorker, LinearGradientPaint] author: aterai pubdate: 2019-12-30T00:46:24+09:00 description: JProgressBarの形状をドーナツ状の半円に変更して、その値に応じてゲージの色を変更するスピードメーターを作成します。 image: https://drive.google.com/uc?id=1MSxJ37yRyZGWHdJFNWAWMYXdEfyZyixR hreflang: href: https://java-swing-tips.blogspot.com/2019/12/create-speedometer-by-changing-shape-of.html lang: en --- * 概要 [#summary] `JProgressBar`の形状をドーナツ状の半円に変更して、その値に応じてゲージの色を変更するスピードメーターを作成します。 #download(https://drive.google.com/uc?id=1MSxJ37yRyZGWHdJFNWAWMYXdEfyZyixR) * サンプルコード [#sourcecode] #code(link){{ class SolidGaugeUI extends BasicProgressBarUI { private final int[] pallet; private final double extent; protected SolidGaugeUI(int range, double extent) { super(); this.pallet = makeGradientPallet(range); this.extent = extent; } @Override public void paint(Graphics g, JComponent c) { Rectangle rect = SwingUtilities.calculateInnerArea(progressBar, null); if (rect.isEmpty()) { return; } Graphics2D g2 = (Graphics2D) g.create(); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // double extent = -150d; double start = 90d + extent * .5; double degree = extent * progressBar.getPercentComplete(); double or = Math.min(rect.width, rect.height); double cx = rect.getCenterX(); double cy = rect.getMaxY(); double sz = or * 2d; double ir = or * .6; Shape inner = new Arc2D.Double(cx - ir, cy - ir, ir * 2d, ir * 2d, start, -extent, Arc2D.PIE); Shape outer = new Arc2D.Double(cx - or, cy - or, sz, sz, start, -extent, Arc2D.PIE); Shape sector = new Arc2D.Double(cx - or, cy - or, sz, sz, start, -degree, Arc2D.PIE); Area foreground = new Area(sector); Area background = new Area(outer); Area hole = new Area(inner); foreground.subtract(hole); background.subtract(hole); // Draw the track g2.setPaint(new Color(0xDD_DD_DD)); g2.fill(background); // Draw the circular sector g2.setPaint(getColorFromPallet(pallet, progressBar.getPercentComplete())); g2.fill(foreground); // Draw minimum, maximum Font font = progressBar.getFont(); float fsz = font.getSize2D(); float min = (float) (cx - or - fsz); float max = (float) (cx + or + 4d); g2.setPaint(progressBar.getForeground()); g2.drawString(Objects.toString(progressBar.getMinimum()), min, (float) cy); g2.drawString(Objects.toString(progressBar.getMaximum()), max, (float) cy); // Deal with possible text painting if (progressBar.isStringPainted()) { float h = (float) cy - fsz; String str = String.format("%d", progressBar.getValue()); float vx = (float) cx - g2.getFontMetrics().stringWidth(str) * .5f; g2.drawString(str, vx, h); float ksz = fsz * 2f / 3f; g2.setFont(font.deriveFont(ksz)); String kmh = "㎞/h"; float tx = (float) cx - g2.getFontMetrics().stringWidth(kmh) * .5f; g2.drawString(kmh, tx, h + ksz); } g2.dispose(); } private static int[] makeGradientPallet(int range) { BufferedImage image = new BufferedImage(range, 1, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); Point2D start = new Point2D.Float(); Point2D end = new Point2D.Float(range - 1f, 0f); float[] dist = {0f, .8f, .9f, 1f}; Color[] colors = {Color.GREEN, Color.YELLOW, Color.ORANGE, Color.RED}; g2.setPaint(new LinearGradientPaint(start, end, dist, colors)); g2.fillRect(0, 0, range, 1); g2.dispose(); int width = image.getWidth(null); int[] pallet = new int[width]; PixelGrabber pg = new PixelGrabber(image, 0, 0, width, 1, pallet, 0, width); try { pg.grabPixels(); } catch (InterruptedException ex) { ex.printStackTrace(); Toolkit.getDefaultToolkit().beep(); Thread.currentThread().interrupt(); } return pallet; } private static Color getColorFromPallet(int[] pallet, double pos) { if (pos < 0d || pos > 1d) { throw new IllegalArgumentException("Parameter outside of expected range"); } int i = (int) (pallet.length * pos); int max = pallet.length - 1; int index = Math.min(Math.max(i, 0), max); return new Color(pallet[index] & 0x00_FF_FF_FF); } // @Override protected Color getSelectionBackground() { // return new Color(0xAA_75_FF_3C, true); // a progress string color // } } }} * 解説 [#explanation] 上記のサンプルでは、`JProgressBar`の形状を半円に変更し、その値に応じてゲージの色を変化させるスピードメーター風のコンポーネントを作成しています。 - ドーナツ状の半円は`Arc2D.PIE`を使用 -- `start`(弧の始角)は反時計回りに`180°`を設定し、`extent`(弧の角)は時計周りなので`0°`からマイナス`180°`まで -- `start`(弧の始角)は反時計回りに`180°`を設定し`extent`(弧の角)は時計周りなので`0°`からマイナス`180°`まで -- 参考: [[JProgressBarの進捗状況を円形で表示する>Swing/ProgressCircle]] - 現在値の表示は`JProgressBar`の`setString(...)`メソッドで設定可能な進捗文字列が`html`タグなどに対応していないので使用せず、独自に`BasicProgressBarUI#paint(...)`メソッド中で値と単位(文字サイズを値の`2/3`に縮小)を`2`行に分けて描画 - 最小値、最大値の描画も`BasicProgressBarUI#paint(...)`メソッド中で描画 - 値に応じた色の変更は`LinearGradientPaint`から生成したパレットから取得 -- 参考: [[JSliderのスタイルを変更する>Swing/GradientTrackSlider]] * 参考リンク [#reference] - [[JProgressBarの進捗状況を円形で表示する>Swing/ProgressCircle]] - [[JSliderのスタイルを変更する>Swing/GradientTrackSlider]] * コメント [#comment] #comment #comment