プログラムdeタマゴ

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

間違いを正しく間違って描画する

 

    @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);
    }
前回載せた間違ったソースです。
これを実行すると、このパネルに載せたボタンも透明になってしまいます。

しかし、Swingの機能で一番上にあるコンポーネントのみを再描画するため、二回目以降の描画では半透明にならず見た目正しく描画されてしまいました。

さて、これを正してみませう。
 
1.コンポーネントを半透明にする。
これが一番簡単な方法です。

JButton b = new JButton("test1"); b.setOpaque(false);
単純な考えで、透明だったら下から描画しなきゃいけないだろうと言う事です。これで簡単に親要素をダーティーリジョンに登録できます。
ただ、これでうまくいかない場合があります。
コンポーネントの中に不透明なコンポーネントがある。しかも外からいじれない時です。
 
さて、これを何とか是正してみましょう。方法は2通りぐらいあります。
1repaint要求を自分で変更する
2RepaintManagerを作成する。
 
1も1も考え方は同じで、再描画される前に描画対象を変更してしまいます。
1は載せる物全部に対して自分で再描画時の対象変更を書かないといけないから部品がいっぱいあると論外。また、privateで外から弄ることの出来ない不透明コンポーネントに対しては結局変更できないパネェ欠点がある
2はすっきりしてるし、簡単だし、外から変更できないコンポーネントにも有効だけど、RepaintManagerは全体で一つしか使えないというパネェ欠点がある
 
パーツが少なくて、かつ、不透明コンポーネントは全て外から変更できる場合は1を選んだ方が他の場所でもRepaintManagerを変更したいときとかに問題が起こりません。
が、上記の条件を一つでも満たさない限りは?を選ぶしかありません。
 
さて、1,2共通して行っておくことがあります。
今回JButtonを載せるパネルはTestというJPanelを拡張したクラスになっていますが、これに以下の関数を追加します。
@Override
public void paint(Graphics g){
paintComponent(g);
paintComponents(g);
}
こうしておかないと、paintが呼ばれても、Swingの機能でpaintComponentがすっ飛ばされてしまうのです。

さて、1の方から見ていきます。
Testに追加するJButtonを以下の様に書き換えます。

        JButton b = new JButton("test1"){
            Test parent=Test.this;

            @Override
            public void repaint(long tm, int x, int y, int w, int h) {
                if(parent == null){
                    super.repaint(tm,x,y,w,h);
                    return;
                }
                Point p = SwingUtilities.convertPoint(this,x,y,parent);
                parent.repaint(tm,p.x,p.y,w,h);
            }
        };

  この様にrepaintを呼ばれると、parentのrepaintが呼ばれる様に変更してしまいます。
 
次に2のRepaintManagerを使った方法。
RepaintManagerは描画される前に処理を横取りして描画対象を変更することが出来ます。
便利なのですが、全体で1つしか使えないのが使いにくい。
 
以下、RepaintManagerを使った全ソースコード。
import java.awt.AlphaComposite;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;

public class Test extends JPanel implements Runnable{
    Test(){
        setLayout(new FlowLayout());
        JButton b = new JButton("test1");
        add(b);
        RepaintManager.setCurrentManager(new RepaintManager2());
    }

    class RepaintManager2 extends RepaintManager{
        @Override
        public void addDirtyRegion(JComponent c, int x, int y, int w,int h) {
            Container parent=c.getParent();
            while(parent!=null){
                if(!parent.isVisible())return;
                if(parent instanceof Test){
                    Point p = SwingUtilities.convertPoint(c,x,y,parent);
                    x = p.x;
                    y = p.y;
                    c = (JComponent)parent;
                    //Testは複数あるかも知れないからbreakはしません
                }
                parent = parent.getParent();
            }
            super.addDirtyRegion(c,x,y,w,h);
        }
    }

    @Override
    public void paint(Graphics g) {
        paintComponent(g);
        paintComponents(g);
    }
    @Override
    public Dimension getPreferredSize() {
        Dimension k =super.getPreferredSize();
        k.width += 30;
        return k;
    }
    @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);
    }

    public void run(){
        JFrame frame = new JFrame("test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(this);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Test());
    }
}