Swing/SemicircleGauge のバックアップ(No.3)
- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- Swing/SemicircleGauge へ行く。
- 1 (2019-12-30 (月) 00:46:55)
- 2 (2020-01-09 (木) 19:11:31)
- 3 (2021-07-07 (水) 20:36:37)
- 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
概要
JProgressBar
の形状をドーナツ状の半円に変更して、その値に応じてゲージの色を変更するスピードメーターを作成します。
Screenshot
Advertisement
サンプルコード
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
// }
}
View in GitHub: Java, Kotlin解説
上記のサンプルでは、JProgressBar
の形状を半円に変更し、その値に応じてゲージの色を変化させるスピードメーター風のコンポーネントを作成しています。
- ドーナツ状の半円は
Arc2D.PIE
を使用start
(弧の始角)は反時計回りに180°
を設定し、extent
(弧の角)は時計周りなので0°
からマイナス180°
まで- 参考: JProgressBarの進捗状況を円形で表示する
- 現在値の表示は
JProgressBar
のsetString(...)
メソッドで設定可能な進捗文字列がhtml
タグなどに対応していないので使用せず、独自にBasicProgressBarUI#paint(...)
メソッド中で値と単位(文字サイズを値の2/3
に縮小)を2
行に分けて描画 - 最小値、最大値の描画も
BasicProgressBarUI#paint(...)
メソッド中で描画 - 値に応じた色の変更は
LinearGradientPaint
から生成したパレットから取得