Swing/AnimeIcon のバックアップの現在との差分(No.16)
TITLE:Timerでアニメーションするアイコンを作成
Posted by aterai at 2006-03-13
Timerでアニメーションするアイコンを作成
javax.swing.Timerを使って、アニメーションするアイコンを作成します。- category: swing folder: AnimeIcon title: Timerでアニメーションするアイコンを作成 tags: [Timer, Animation, Icon] author: aterai pubdate: 2006-03-13T00:29:10+09:00 description: javax.swing.Timerを使って、アニメーションするアイコンを作成します。 image:
概要
javax.swing.Timer
を使って、アニメーションするアイコンを作成します。
- &jnlp;
- &jar;
- &zip;
Screenshot
Advertisement
サンプルコード
#spanend
#spanadd
* サンプルコード [#sourcecode]
#spanend
#spanadd
#code(link){{
#spanend
class AnimatedLabel extends JLabel implements ActionListener {
private final javax.swing.Timer animator;
private final Timer animator;
private final AnimeIcon icon = new AnimeIcon();
public AnimatedLabel() {
super();
animator = new javax.swing.Timer(100, this);
animator = new Timer(100, this);
setIcon(icon);
addHierarchyListener(new HierarchyListener() {
@Override public void hierarchyChanged(HierarchyEvent e) {
if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0
&& !e.getComponent().isDisplayable()) {
stopAnimation();
}
}
});
}
public void actionPerformed(ActionEvent e) {
#spanadd
#spanend
@Override public void actionPerformed(ActionEvent e) {
icon.next();
repaint();
}
#spanadd
#spanend
public void startAnimation() {
icon.setRunning(true);
animator.start();
}
#spanadd
#spanend
public void stopAnimation() {
icon.setRunning(false);
animator.stop();
}
}
#spandel
class AnimeIcon implements Icon {
#spanend
private static final Color cColor = new Color(0.5f,0.5f,0.5f);
private final List<Shape> list = new ArrayList<Shape>();
private final Dimension dim;
private boolean isRunning = false;
public AnimeIcon() {
super();
double r = 2.0d;
double sx = 1.0d;
double sy = 1.0d;
list.add(new Ellipse2D.Double(sx+3*r, sy+0*r, 2*r, 2*r));
list.add(new Ellipse2D.Double(sx+5*r, sy+1*r, 2*r, 2*r));
list.add(new Ellipse2D.Double(sx+6*r, sy+3*r, 2*r, 2*r));
list.add(new Ellipse2D.Double(sx+5*r, sy+5*r, 2*r, 2*r));
list.add(new Ellipse2D.Double(sx+3*r, sy+6*r, 2*r, 2*r));
list.add(new Ellipse2D.Double(sx+1*r, sy+5*r, 2*r, 2*r));
list.add(new Ellipse2D.Double(sx+0*r, sy+3*r, 2*r, 2*r));
list.add(new Ellipse2D.Double(sx+1*r, sy+1*r, 2*r, 2*r));
dim = new Dimension((int)(r*8+sx*2), (int)(r*8+sy*2));
#spanadd
#spanend
#spanadd
class AnimeIcon implements Icon, Serializable {
#spanend
private static final long serialVersionUID = 1L;
private static final Color ELLIPSE_COLOR = new Color(.5f, .5f, .5f);
private static final double R = 2d;
private static final double SX = 1d;
private static final double SY = 1d;
private static final int WIDTH = (int) (R * 8 + SX * 2);
private static final int HEIGHT = (int) (R * 8 + SY * 2);
private final List<Shape> list = new ArrayList<Shape>(Arrays.asList(
new Ellipse2D.Double(SX + 3 * R, SY + 0 * R, 2 * R, 2 * R),
new Ellipse2D.Double(SX + 5 * R, SY + 1 * R, 2 * R, 2 * R),
new Ellipse2D.Double(SX + 6 * R, SY + 3 * R, 2 * R, 2 * R),
new Ellipse2D.Double(SX + 5 * R, SY + 5 * R, 2 * R, 2 * R),
new Ellipse2D.Double(SX + 3 * R, SY + 6 * R, 2 * R, 2 * R),
new Ellipse2D.Double(SX + 1 * R, SY + 5 * R, 2 * R, 2 * R),
new Ellipse2D.Double(SX + 0 * R, SY + 3 * R, 2 * R, 2 * R),
new Ellipse2D.Double(SX + 1 * R, SY + 1 * R, 2 * R, 2 * R)));
#spanadd
#spanend
private boolean isRunning;
public void next() {
if (isRunning) {
list.add(list.remove(0));
}
}
public int getIconWidth() {
return dim.width;
#spanadd
#spanend
public void setRunning(boolean isRunning) {
this.isRunning = isRunning;
}
public int getIconHeight() {
return dim.height;
}
public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint((c!=null)?c.getBackground():Color.WHITE);
g2d.fillRect(x, y, getIconWidth(), getIconHeight());
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(cColor);
float alpha = 0.0f;
g2d.translate(x, y);
for(Shape s: list) {
alpha = isRunning?alpha+0.1f:0.5f;
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
g2d.fill(s);
#spanadd
#spanend
@Override public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setPaint(c == null ? Color.WHITE : c.getBackground());
g2.fillRect(x, y, getIconWidth(), getIconHeight());
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(ELLIPSE_COLOR);
g2.translate(x, y);
int size = list.size();
for (int i = 0; i < size; i++) {
float alpha = isRunning ? (i + 1) / (float) size : .5f;
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, alpha));
g2.fill(list.get(i));
}
g2d.translate(-x, -y);
// g2.translate(-x, -y);
g2.dispose();
}
public void next() {
if(isRunning) list.add(list.remove(0));
#spanadd
#spanend
@Override public int getIconWidth() {
return WIDTH;
}
public void setRunning(boolean isRunning) {
this.isRunning = isRunning;
#spanadd
#spanend
@Override public int getIconHeight() {
return HEIGHT;
}
}
解説
上記のサンプルでは、スタートボタンを押すと*1アイコンがFireFox風にアニメーションします。解説
上記のサンプルでは、スタートボタンを押すと(JTextArea
に表示している作業状況はランダムにThread.sleep(...)
を実行して時間稼ぎしている)アイコンがFireFox
風にアニメーションします。
アニメーションは、8個の円からアイコンを生成して、それぞれのインデックスを順に変更することで行っています。
アニメーションは、8
個の小さな円からアイコンを生成して、それぞれのインデックスを順に変更することで行っています。
- アイコンの生成
- リスト(Vector)に、座標の異なる円(Ellipse2D.Double)を8個生成して追加します。
- リスト(
ArrayList
)に、座標の異なる円(Ellipse2D.Double
)を8
個生成して追加
- インデックスの変更
- Timerを使って、リストの先頭にある円を最後に移動します。
-
Timer
を使って、リストの先頭にある円を最後に移動
- 異なるアルファ値で円を描画
- インデックスに応じたアルファ値でそれぞれの円を描画します。
- インデックスに応じたアルファ値でそれぞれの円を描画
-
Java 1.6.0
では「小さな円(曲線)が円に見えなかった問題」が解消されている(参考)
- -
一々自分で座標を計算して
new Ellipse2D
を8
個も並べるのを避けたい、またはもうすこし正確に円を配置したい場合は、AffineTransform
などを使ってアイコンを生成する方法もあります。
- -
一々自分で座標を計算してnew Ellipse2D を8個も並べるのが嫌だったり、もうすこし正確に円を配置したい場合は、AffineTransformなどを使ってアイコンを生成する方法もあります*2。
#spandel class AnimeIcon implements Icon { #spanend private static final Color cColor = new Color(0.5f,0.8f,0.5f); //private final Vector<Shape> list = new Vector<Shape>(); private final List<Shape> list = new ArrayList<Shape>(); #spanadd class AnimeIcon2 implements Icon, Serializable { #spanend private static final long serialVersionUID = 1L; private static final Color ELLIPSE_COLOR = new Color(.5f, .8f, .5f); private final List<Shape> list = new ArrayList<>(); private final Dimension dim; private boolean isRunning = false; //int rotate = 45; public AnimeIcon() { private boolean isRunning; public AnimeIcon2() { super(); int r = 4; Shape s = new Ellipse2D.Float(0, 0, 2*r, 2*r); for(int i=0;i<8;i++) { AffineTransform at = AffineTransform.getRotateInstance(i*2*Math.PI/8); Shape s = new Ellipse2D.Float(0, 0, 2 * r, 2 * r); for (int i = 0; i < 8; i++) { AffineTransform at = AffineTransform.getRotateInstance( i * 2 * Math.PI / 8); at.concatenate(AffineTransform.getTranslateInstance(r, r)); list.add(at.createTransformedShape(s)); } //int d = (int)(r*2*(1+2*Math.sqrt(2))); int d = (int)r*2*(1+3); // 2*Math.sqrt(2) is nearly equal to 3. // int d = (int) (r * 2*(1 + 2 * Math.sqrt(2))); int d = (int) r * 2 * (1 + 3); // 2 * Math.sqrt(2) is nearly equal to 3. dim = new Dimension(d, d); } public int getIconWidth() { #spanadd #spanend @Override public int getIconWidth() { return dim.width; } public int getIconHeight() { #spanadd #spanend @Override public int getIconHeight() { return dim.height; } public void paintIcon(Component c, Graphics g, int x, int y) { Graphics2D g2d = (Graphics2D) g; g2d.setPaint((c!=null)?c.getBackground():Color.WHITE); g2d.fillRect(x, y, getIconWidth(), getIconHeight()); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setColor(cColor); float alpha = 0.0f; int xx = x + dim.width/2; int yy = y + dim.height/2; //AffineTransform at = AffineTransform.getRotateInstance(Math.toRadians(rotate), xx, yy); //at.concatenate(AffineTransform.getTranslateInstance(xx, yy)); g2d.translate(xx, yy); for(Shape s: list) { alpha = isRunning?alpha+0.1f:0.5f; g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); //g2d.fill(at.createTransformedShape(s)); g2d.fill(s); #spanadd #spanend @Override public void paintIcon(Component c, Graphics g, int x, int y) { Graphics2D g2 = (Graphics2D) g.create(); g2.setPaint(c == null ? Color.WHITE : c.getBackground()); g2.fillRect(x, y, getIconWidth(), getIconHeight()); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(ELLIPSE_COLOR); int xx = x + dim.width / 2; int yy = y + dim.height / 2; g2.translate(xx, yy); int size = list.size(); for (int i = 0; i < size; i++) { float alpha = isRunning ? (i + 1) / (float) size : .5f; g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, alpha)); g2.fill(list.get(i)); } // g2.translate(-xx, -yy); g2.dispose(); } #spanadd #spanend public void next() { if(isRunning) { if (isRunning) { list.add(list.remove(0)); //rotate = rotate<360?rotate+45:45; } } #spanadd #spanend public void setRunning(boolean isRunning) { this.isRunning = isRunning; } }
コメント
- 色の濃い円が時計回りに回転するように変更しました。 -- aterai
- ロードインジケータと呼ぶらしい。 -- aterai
-
このサンプルでは、Swing Tutorialにあった古いSwingWorkerを使用しているけど、https://swingworker.dev.java.net/ にある JDK 1.6 からバックポートされた org.jdesktop.swingworker.SwingWorker を使用したほうがいいかも。そのうち修正する予定するように変更しました。 -- aterai