プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

Graphicsの設定を弄るべからず

今日はJAVA内部にまで踏み込んだ画面描写についての濃い内容を書こうかなと思っています。Synth Look and Feelとかについては私も分からないので触れません。
結構濃い内容だから投稿を数回に分けると思ふ。JOGLについては、数ヶ月後に悶々書き並べることになると思うので、今のところは放置しておきましょう。(JOGLの日本語資料が欲しくてやってきた人はごめんなさい。テクスチャ以外はJOGLで調べるよりOpenGLで調べると良いよ。)
 
ちなみに、昨日の失敗を反省し、今日はwriterで書いてるよ。
 
いきなり深い内部にまで踏み込んでもしょうがないので、今日は浅いところを。
 
SwingではAWTと違って画面の描写にpaint関数をオーバーライドするのではなくpaintComponentをオーバーライドするのはpaintは子コンポーネントを描画する処理が組み込まれているからです。だから、ここを変えてしまうとちゃんとした描画が出来なくなる恐れがあります。paintをオーバーライドするときは基本的に、子コンポーネントの描画に影響を与えたいとき(全部半透明にしたい、線形変換したい、描画し終わった後に上に何か描き足したいなど)にのみ行います。その時はかならすsuper.paint(g)を呼び出して下さい。
自分の描画に関しては必ずpaintComponentをオーバーライドして下さい。
 
よっしゃ、paintComponentで自前の描画をするんだな!オケーオケー、分かったぜ!
かっこよく半透明の文字でも書き込んだらぁ!

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f));
        g2.drawString("俺に任せろー!バリバリー!",0,20);
    }

(JPanelのpaintComponentをオーバーライド)

OK!OK!
一度は誰もが通る道。(たぶん)
では、試しにこのJPanelにJbuttonを一つのっけてから実行してみましょう。
 

あるぇ?(´・ω・`)
 
paintComponentをオーバーライドしたのに子コンポーネントのJButtonまで半透明になっています。
 
実は、paintComponentに渡されるGraphics(2D)オブジェクトは画面描画の際、全ての子コンポーネントで使い回されているのです。
 
Graphicsオブジェクトには状態という物があります。
draw関数で描画するための色だとかフォントだとか、今回設定したcompositとか、座標系の設定とかです。
実はその状態は一度設定するとクリアされません*1
だから、親コンポーネントのpaintComponentで設定してしまったCompositが子にも引き継がれてしまったのです。
 
正しくは以下の様に書きます。
 
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g.create(); //コピーの作成 
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f));
        g2.drawString("俺に任せろー!バリバリー!",0,20);
        g2.dispose(); //使い終わったら解放してあげる
    }

Graphicsの状態を書き換える前にそのGrahpicsのコピーを作成します。
このコピーした物は状態とかは元のGraphicsと共有しませんが、書き込み先は同じです。よって、このコピーした物を使って書いても、もとのGraphicsで書いてもちゃんと同じ所に描画されます。
こうすることで、状態を変えることなく子コンポーネントにつなげることが出来ます。
作ったGrahicsオブジェクトは必ず自分で解放しましょう。
 

ちゃんと意図した様に描画されましたね。

*1:Color属性とFont属性と原点座標だけはJComponentのpaintで設定し直してくれます。気にせずに変えましょう。