JWindow
共有する画面の領域を選択するために、に基づいてカスタムUIを作成しようとしています。JWindow
サイズを変更し、を使用してウィンドウの中央を「切り取る」ために、コードを拡張して追加しましAWTUtilities.setWindowShape()
た。
コードを実行すると、ウィンドウが負のx方向とy方向、つまり上下にサイズ変更されるため、ちらつきが発生します。発生しているように見えるのは、コンポーネントが更新される前にウィンドウのサイズが変更されて描画されることです。以下は、コードの簡略化されたバージョンです。実行すると、トップパネルを使用してウィンドウのサイズを左上に変更できます。ウィンドウの背景は緑色に設定されており、表示したくないピクセルがどこにあるかが明確になっています。
編集:を使用してウィンドウを正しく整形するようにコードを改善し、ComponentListener
ちらつきをさらに説明するために下部にダミーコンポーネントを追加しました(スクリーンショットも更新されました)。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Area;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.border.LineBorder;
import com.sun.awt.AWTUtilities;
public class FlickerWindow extends JWindow implements MouseListener, MouseMotionListener{
JPanel controlPanel;
JPanel outlinePanel;
int mouseX, mouseY;
Rectangle windowRect;
Rectangle cutoutRect;
Area windowArea;
public static void main(String[] args) {
FlickerWindow fw = new FlickerWindow();
}
public FlickerWindow() {
super();
setLayout(new BorderLayout());
setBounds(500, 500, 200, 200);
setBackground(Color.GREEN);
controlPanel = new JPanel();
controlPanel.setBackground(Color.GRAY);
controlPanel.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
controlPanel.addMouseListener(this);
controlPanel.addMouseMotionListener(this);
outlinePanel = new JPanel();
outlinePanel.setBackground(Color.BLUE);
outlinePanel.setBorder(new CompoundBorder(new EmptyBorder(2,2,2,2), new LineBorder(Color.RED, 1)));
add(outlinePanel, BorderLayout.CENTER);
add(controlPanel, BorderLayout.NORTH);
add(new JButton("Dummy button"), BorderLayout.SOUTH);
setVisible(true);
setShape();
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
setShape();
}});
}
public void paint(Graphics g) {
// un-comment or breakpoint here to see window updates more clearly
//try {Thread.sleep(10);} catch (Exception e) {}
super.paint(g);
}
public void setShape() {
Rectangle bounds = getBounds();
Rectangle outlineBounds = outlinePanel.getBounds();
Area newShape = new Area (new Rectangle(0, 0, bounds.width, bounds.height));
newShape.subtract(new Area(new Rectangle(3, outlineBounds.y + 3, outlineBounds.width - 6, outlineBounds.height - 6)));
setSize(bounds.width, bounds.height);
AWTUtilities.setWindowShape(this, newShape);
}
public void mouseDragged(MouseEvent e) {
int dx = e.getXOnScreen() - mouseX;
int dy = e.getYOnScreen() - mouseY;
Rectangle newBounds = getBounds();
newBounds.translate(dx, dy);
newBounds.width -= dx;
newBounds.height -= dy;
mouseX = e.getXOnScreen();
mouseY = e.getYOnScreen();
setBounds(newBounds);
}
public void mousePressed(MouseEvent e) {
mouseX = e.getXOnScreen();
mouseY = e.getYOnScreen();
}
public void mouseMoved(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
オーバーライドさpaint()
れたメソッドは、ブレークポイントとして使用するThread.sleep()
ことも、コメントを外して、更新が発生したときにそれをより明確に表示することもできます。
私の問題は、setBounds()
レイアウトする前にウィンドウを画面にペイントする方法に起因しているようです。
サイズ変更前のウィンドウ。見た目は次のとおりです。
オーバーライドされたメソッドのブレークポイントで見られるように、サイズ変更中のウィンドウ(上と左)が大きくなりますpaint()
):
オーバーライドされたメソッドのブレークポイントで見られるように、小さいサイズ(下と右)のサイズ変更中のウィンドウpaint()
):
確かに、これらのスクリーンショットは積極的なマウスドラッグの動きの間に撮影されますが、より穏やかなマウスドラッグでもちらつきは非常に厄介になります。
大きなスクリーンショットへのサイズ変更の緑色の領域は、ペイント/レイアウトが完了する前に描画される新しい背景を示しています。これは、基になるComponentPeer
ウィンドウマネージャーまたはネイティブウィンドウマネージャーで発生しているようです。'サイズ変更'スクリーンショットの青い領域は、JPanel
'背景が表示されていることを示していますが、現在は古くなっています。これは、Linux(Ubuntu)およびWindowsXPで発生します。
画面に変更を加える前に、またはをバックバッファにサイズ変更して、このちらつきの影響を回避Window
する方法を見つけた人はいますか?これを回避するために設定できるシステムプロパティJWindow
があるかもしれませんが、見つかりませんでした。java.awt....
編集#2:ちらつきの性質を明確に確認するために、呼び出しをコメントアウトしAWTUtilities.setWindowShape()
(オプションでThread.sleep(10)
行のコメントを解除しpaint()
)、トップパネルを積極的にドラッグします。
編集#3: Windows7またはMacOSXのSunJavaでこの動作をテストできる人はいますか?