それでは、これをフレームにしたてましょう。 まずは JFrame にしたてために JFrame で定義されているコンストラクタをすべて定義します。また、RootPane に描画を行うコンポーネントをセットしましょう。描画位置のずれも一緒に直してしまいましょう。
位置のずれは描画領域以外の部分がどの程度の大きさなのか知らなくてはいけません。この情報は Insets クラスを使用します。
class ImageRootPane extends JRootPane {
public void paintComponent(Graphics g) {
Insets insets = SimpleTransFrame1.this.getInsets();
g.drawImage(image, -insets.left, -insets.top, this);
}
} | 次はフレームの大きさを自由に変更できるようにしましょう。
今まではフレームの位置とサイズは固定だったので、いつでもキャプチャできたのですが、可変にするとそうはいきません。 一般に GUI コンポーネントのサイズが決まるのは addNotify メソッドがコールされるときなので、ここでもそうしています。
public void addNotify() {
super.addNotify();
Rectangle bounds = getBounds();
Insets insets = getInsets();
bounds = new Rectangle(bounds.x + insets.left,
bounds.y + insets.top,
bounds.width - insets.left - insets.right,
bounds.height - insets.top - insets.bottom);
image = robot.createScreenCapture(bounds);
} | ただ、これだと setVisble(true) メソッドをコールする前にサイズや位置を変えるのであればいいのですが、一度フレームを表示してしまってからサイズを変更しても反映されません。というのも addNotify メソッドは表示されるときに 1 度しかコールされないからです。 フレームが他のアプリケーションに隠された後にまた表示したり、サイズを変更すると、OS から再描画するようにアプリケーションに通知されます。このとき、repaint メソッドがコールされるかなと思っていたのですが、どうやらコールされないようです。repaint メソッドはあくまでもアプリケーション内部で再描画を行うときに使われるためだと思います。 何かないかなと JavaDoc を探していたら、なにやらにおうメソッドが... そのメソッドは getIgnoreRepaint メソッドです。getIgnoreRepaint メソッドは J2SE 1.4 から導入されたメソッドで、OS からの再描画を無視するかどうかを設定するためのメソッドです。 試してみると、OS から再描画があったときには毎回このメソッドがコールされているようです。そこで、このメソッドに
public boolean getIgnoreRepaint() {
Rectangle bounds = getBounds();
Insets insets = getInsets();
bounds = new Rectangle(bounds.x + insets.left,
bounds.y + insets.top,
bounds.width - insets.left - insets.right,
bounds.height - insets.top - insets.bottom);
image = robot.createScreenCapture(bounds);
return super.getIgnoreRepaint();
} | とやってみたのですが、うまくキャプチャできません。というかキャプチャしているんだけど、違うものをキャプチャしているのです。 ようするに自分自身をキャプチャしています。フレームが表示されているのですから当たり前です。 そこで、一度フレームを非表示にして、キャプチャ、表示ということしてみましょう。
public boolean getIgnoreRepaint() {
Rectangle bounds = getBounds();
Insets insets = getInsets();
bounds = new Rectangle(bounds.x + insets.left,
bounds.y + insets.top,
bounds.width - insets.left - insets.right,
bounds.height - insets.top - insets.bottom);
hide();
image = robot.createScreenCapture(bounds);
show();
return super.getIgnoreRepaint();
} | これを実行すると.... うんともすんともいいません。デッドロックに陥ってしまっているようです。 まぁ、当たり前といえば当たり前ですが ^^;; 再描画の部分で hide していて、その上、再表示までしているのですから。 それじゃあ、ということでこの処理は別スレッドで行いましょう。 こういう処理を行うには wait - notifyAll を使うのが常套手段です。
通常は wait で眠っており、誰かが notifyAll メソッドで眠っているスレッドを起こしたらキャプチャを行うようにします。notifyAll メソッド以外に notify メソッドもありますが、それよりは notifyAllメソッドを使ったほうが確実です。 wait メソッド、notifyAll メソッドをコールするには synchronized をする必要があります。
class Watcher extends Thread {
public synchronized void wakeup() {
notifyAll();
}
public void run() {
try {
while(true) {
synchronized (this) {
wait();
}
copyScreen();
}
} catch (InterruptedException ex) {
return;
}
}
} | copyScreen メソッドが実際にキャプチャをするメソッドです。
public void copyScreen() {
Rectangle bounds = getBounds();
Insets insets = getInsets();
bounds = new Rectangle(bounds.x + insets.left,
bounds.y + insets.top,
bounds.width - insets.left - insets.right,
bounds.height - insets.top - insets.bottom);
hide();
image = robot.createScreenCapture(bounds);
show();
} | getIgnoreRepaint メソッドと addNotify メソッドはこうなります。
public boolean getIgnoreRepaint() {
watcher.wakeup();
return super.getIgnoreRepaint();
}
public void addNotify() {
watcher.wakeup();
super.addNotify();
} | watcher が Watcher クラスのオブジェクトです。 これで、サイズが可変になり、再描画でも再キャプチャできるようになりました。 |