1

JButton の不透明ピクセルでのイベント検出

上記の私の質問にあるコード例を使用して、連動する不規則なエッジを持ついくつかのボタンを作成し、ボタンを適切に配置するためにヌル レイアウトを使用しています。私が遭遇している問題は、バッファリングされた画像の透明なピクセルでマウスのクリックが検出されていないにもかかわらず、ボタンがまだ長方形の形をとっていることです。これは、後でパネルに追加されたボタンが、隣接するボタンの一部をブロックすることを意味します。

私の質問は、不透明なピクセルになるまでマウスイベントを強制的に JButton の物理的な配置全体に伝播させる方法はありますか、それとも別の解決策が必要ですか? Shape を含むソリューションを見てきましたが、非常に高価に見えるため、別の方法を考えています。

JButton をそのままにしておく必要がある場合は、JButton を使用することにあまり執着していませんが、安価な解決策があればそれを見つけたいと思っています。

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;


public class JButtonExample {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                final JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                MyButton button1 = null, button2 = null;

                try {
                    button1 = new MyButton(ImageIO.read(new URL("https://dl.dropbox.com/s/dxbao8q0xeuzhgz/button1.png")));
                } catch (Exception ex) {
                    ex.printStackTrace();
                }

                button1.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent me) {
                        super.mouseClicked(me);
                        MyButton mb = ((MyButton) me.getSource());
                        if (!isAlpha(mb.getIconImage(), me.getX(), me.getY()))
                            JOptionPane.showMessageDialog(frame, "You clicked button 1");
                    }

                    private boolean isAlpha(BufferedImage bufImg, int posX, int posY) {
                        int alpha = (bufImg.getRGB(posX, posY) >> 24) & 0xFF;
                        return alpha == 0 ? true : false;
                    }
                });

                button1.setBounds(10, 10, 72, 77);

                try {
                    button2 = new MyButton(ImageIO.read(new URL("https://dl.dropbox.com/s/v16kyha0ojx1gza/button2.png")));
                } catch (Exception ex) {
                    ex.printStackTrace();
                }

                button2.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent me) {
                        super.mouseClicked(me);
                        MyButton mb = ((MyButton) me.getSource());
                        if (!isAlpha(mb.getIconImage(), me.getX(), me.getY()))
                            JOptionPane.showMessageDialog(frame, "You clicked button 2");
                    }

                    private boolean isAlpha(BufferedImage bufImg, int posX, int posY) {
                        int alpha = (bufImg.getRGB(posX, posY) >> 24) & 0xFF;
                        return alpha == 0 ? true : false;
                    }
                });

                button2.setBounds(65, 0, 122, 69);

                frame.getContentPane().setLayout(null);

                frame.add(button1);
                frame.add(button2);

                frame.setSize(210, 130);
                frame.setVisible(true);
            }
        });
    }
}

class MyButton extends JButton {

    BufferedImage icon;

    MyButton(BufferedImage bi) {
        this.icon = ((BufferedImage) bi);
        setContentAreaFilled(false);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(icon.getWidth(), icon.getHeight());
    }

    public BufferedImage getIconImage() {
        return icon;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(icon, 0, 0, null);
        g.dispose();
    }
}
4

1 に答える 1

0

さて、私は自分のユースケースに十分な精度でやりたいことを行うソリューションを考え出しました。詳細のいくつかは少しハックかもしれませんが、私は今、私がやりたいことをやっていると思います. 作成するコードはここAreaから適応されます。

大規模なプログラムの他の領域での単純さと使いやすさのために、各ボタンを独立した として保持しましたJButton。いずれかのボタンでマウス クリックが検出されると、親パネルに表示されるクリックの位置が計算され、その位置が として親に渡されPointます。次に、親は、ポイントを含むボタンが見つかるまでボタンの配列を実行し、適切なメソッドを起動します。クリックがボタンに含まれる領域内にない場合、効果はありません。元のマウス クリックが a を囲む正方形領域で発生しない場合、JButton処理は行われません。

を生成するために必要な計算は、プログラムの残りの部分に比べて比較的コストがかかりますが、起動時にAreaすべてのオブジェクトを作成することでこれに対処できます。Area使用済み。

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;


public class Example {

    private static int[][] pos = {{10, 10, 72, 77}, {65, 0, 122, 69}};

    public static MyButton[] buttons;

    private static URL[] src = new URL[2]; 

    private static MapPanel pane;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                final JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                try{
                    src[0] = new URL("https://dl.dropbox.com/s/dxbao8q0xeuzhgz/button1.png");
                    src[1] = new URL("https://dl.dropbox.com/s/v16kyha0ojx1gza/button2.png");
                } catch (Exception e){
                    e.printStackTrace();
                    System.exit(0);
                }

                pane = new MapPanel();
                pane.setLayout(null);

                buttons = new MyButton[2];

                for(int i = 0 ; i < buttons.length ; i++){
                    final int j = i;
                    try{
                        buttons[j] = new MyButton((ImageIO.read(src[j])), j, pos[j][0], pos [j][1]);
                    } catch (Exception e){
                        e.printStackTrace();
                        System.exit(0);
                    }
                    buttons[j].addMouseListener(new MouseAdapter(){
                        @Override
                        public void mouseClicked(MouseEvent me){
                            Point p = new Point(me.getX() + buttons[j].getX(), me.getY() + buttons[j].getY());
                            pane.check(p);
                        }
                    });
                    buttons[j].setBounds(pos[j][0], pos[j][1], pos[j][2], pos[j][3]);
                    pane.add(buttons[j]);
                }

                frame.setContentPane(pane);

                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(210, 130);
                frame.setVisible(true);
            }
        });
    }
}

class MapPanel extends JLabel{

    public MapPanel(){
        super();
        this.setOpaque(true);
    }

    public void check(Point p){
    for(int i = 0 ; i < Example.buttons.length ; i++){
    if(Example.buttons[i].contains(p)){
            Example.buttons[i].clickDetected();
            break;
        }
        }
    }
}

class MyButton extends JButton {

    private BufferedImage icon;
    private int x, y, index;
    private Area area;

    MyButton(BufferedImage bi, int index, int x, int y) {
        this.icon = ((BufferedImage) bi);
        this.x = x;
        this.y = y;
        this.index = index;
        setContentAreaFilled(false);
        createArea();
    }

    @Override
    public Dimension getPreferredSize() {
        if(icon != null){
            return new Dimension(icon.getWidth(), icon.getHeight());
        } else {
            return super.getPreferredSize();
            }
    }

    public BufferedImage getIconImage() {
        return icon;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(icon, 0, 0, null);
        g.dispose();
    }

    private void createArea(){      
        GeneralPath gp = new GeneralPath();
        boolean cont = false;

        for(int xx = 0 ; xx < icon.getWidth() ; xx++){
            for(int yy = 0 ; yy < icon.getHeight() ; yy++){
                if(getAlpha(xx, yy) != 0){
                    if(cont){
                        gp.lineTo(xx, yy);
                        gp.lineTo(xx, yy+1);
                        gp.lineTo(xx+1, yy+1);
                        gp.lineTo(xx+1, yy);
                        gp.lineTo(xx, yy);
                    } else{
                        gp.moveTo(xx, yy);
                    }
                    cont = true;
                } else {
                    cont = false;
                }
            }
            cont = false;
        }

        gp.closePath();

        area = new Area(gp);        
    }

    @Override
    public boolean contains(Point p){
        if(area.contains(new Point((int)(p.getX() - this.x), (int) (p.getY() - this.y)))){
            return true;
        }
        return false;
    }

    private int getAlpha(int posx, int posy){
        return(icon.getRGB(posx, posy) >> 24) & 0x000000FF;
    }

    public void clickDetected(){
        JOptionPane.showMessageDialog(null, "You clicked button " + Integer.toString(this.index + 1) + ".");
    }
}

また、このコード (またはコード外) で問題になっていることを誰かが指摘してくれれば、非常にありがたいです。GeneralPath領域を作成する方法は、基準を満たすすべてのピクセル (この場合は非透明ピクセル) の上に 1 ピクセルの正方形を作成し、パスに含まれる領域から領域オブジェクトを作成することであると想定しています。間違っている場合は修正してください。

于 2013-01-08T05:45:05.433 に答える