Summary
JComponentの中央で円形スピナーが回転するアニメーションで、不確定モードのローディング画面コンポーネントを作成します。
Screenshot

Advertisement
Source Code Examples
// A value object that only holds the starting angle
// and sweep angle of the arc for one frame.
final class ArcAngles {
final float startAngle;
final float sweepAngle;
ArcAngles(float startAngle, float sweepAngle) {
this.startAngle = startAngle;
this.sweepAngle = sweepAngle;
}
}
// An abstract class that integrates size management
// and timer management common to the four spinners.
abstract class AbstractCircularSpinner extends JComponent {
protected final float size;
protected final float stroke;
protected final Timer timer = new Timer(16, e -> repaint());
protected final long startTime;
protected AbstractCircularSpinner(float size, float stroke) {
super();
this.size = size;
this.stroke = stroke;
this.startTime = System.currentTimeMillis();
this.timer.start();
}
@Override public Dimension getPreferredSize() {
int totalSize = (int) Math.ceil(size + stroke);
return new Dimension(totalSize, totalSize);
}
@Override public void removeNotify() {
super.removeNotify();
timer.stop();
}
// Template method body
// Only two functions, 'finding the angle' and 'drawing based on the angle',
// are delegated to the subclass.
@Override protected final void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = createValidatedGraphics2D(g);
float x = (getWidth() - size) / 2f;
float y = (getHeight() - size) / 2f;
long elapsed = System.currentTimeMillis() - startTime;
ArcAngles arc = computeArcAngles(elapsed);
paintArc(g2, x, y, arc);
g2.dispose();
}
// Template method 1:
// Find the angle of the arc from the elapsed time
protected abstract ArcAngles computeArcAngles(long elapsedMillis);
// Template method 2:
// Actual drawing after determining the angle
protected abstract void paintArc(Graphics2D g2, float x, float y, ArcAngles arc);
// Common Graphics2D initialization process
protected Graphics2D createValidatedGraphics2D(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(
RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHint(
RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
return g2;
}
// Common ring generation used in Area drawing system
protected Area createRing(float x, float y) {
Area outer = new Area(new Ellipse2D.Float(x, y, size, size));
float innerSize = size - stroke * 2f;
float innerOffset = (size - innerSize) / 2f;
float ax = x + innerOffset;
float ay = y + innerOffset;
Area inner = new Area(new Ellipse2D.Float(ax, ay, innerSize, innerSize));
outer.subtract(inner);
return outer;
}
protected Area createArcArea(Area ring, float startAngle, float sweepAngle) {
float x = (getWidth() - size) / 2f;
float y = (getHeight() - size) / 2f;
float cx = x + size / 2f;
float cy = y + size / 2f;
float r = size / 2f + 1f;
Arc2D arc = new Arc2D.Float(
cx - r, cy - r, r * 2f, r * 2f, startAngle, sweepAngle, Arc2D.PIE);
Area arcSector = new Area(arc);
Area arcArea = new Area(ring);
arcArea.intersect(arcSector);
return arcArea;
}
}
View in GitHub: Java, KotlinDescription
AbstractCircularSpinnerは、以下の4種類の円形スピナーコンポーネントにおける共通処理(Timerによる定期的な再描画、コンポーネントサイズの管理、描画用Graphics2Dの初期化など)をカプセル化した抽象クラスです。
経過時間に応じた角度の算出(computeArcAnglesメソッド)と、実際の描画処理(paintArcメソッド)をサブクラスに委譲します。
- 左上:
SimpleStrokeSpinnerBasicStrokeを使用して円弧(Arc2D)の輪郭線を描画する基本的なスピナー- 経過時間に対して一定の速度で開始角度が回転し、円弧の長さは固定値のまま回転する
- 右上:
SimpleAreaSpinnerAreaクラスの積集合(intersect)を使用して、ドーナツ型の円(createRing)とパイ型の円弧(Arc2D.PIE)が重なる領域を塗りつぶすスピナー- 描画ロジックは異なるが、
SimpleStrokeSpinnerと同様に一定の速度と固定の長さで回転する
- 左下:
MaterialStrokeSpinnerYouTubeやAndroidのMaterial Design風に伸縮しながら回転する不確定モードの進行状況インジケータBasicStrokeを用いた線で描画し、回転速度や円弧の長さが時間経過(イージング)によって滑らかに変化する
- 右下:
MaterialAreaSpinnerMaterialStrokeSpinnerと同様の伸縮・回転アニメーションを、Areaを用いた面塗りつぶしで描画するスピナー- グラデーションの適用や特殊なクリッピングなど、線描画(
Stroke)だけでは表現しづらい複雑な形状のカスタマイズが可能
Reference
- SwingWorkerを使った処理の中断と進捗状況表示
- JProgressBarの進捗状況を円形で表示する
- Circular Indeterminate ProgressBar | Android UX Tutorial - YouTube