java - 消除方形边缘按钮java

标签 java swing jbutton bufferedimage shapes

Event detection on opaque pixels in 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();
    }
}

最佳答案

好吧,我已经制定了一个解决方案,可以按照我的使用场景足够准确地完成我想要的操作。虽然有些细节可能有点老套,但我想我现在已经让它做我想做的事了。创建Area的代码改编自here .

为了在大型程序的其他区域中简单易用,我将每个按钮保留为独立的 JButton。当在任何按钮上检测到鼠标单击时,它会计算父面板上显示的单击位置,然后将该位置作为 Point 传递给父面板。然后,父级遍历按钮数组,直到找到包含该点的按钮,并触发适当的方法。如果单击不在任何按钮所包含的区域内,则没有任何效果。如果原始鼠标单击没有发生在限定 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 创建区域的方式是在满足条件的每个像素(在本例中为非透明像素)顶部创建一个 1px 的正方形,然后从路径包含的区域。如果我错了,请纠正我。

关于java - 消除方形边缘按钮java,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14190430/

相关文章:

java - JPA REQUIRES_NEW 事务到底什么时候提交

java - Maven 程序集 :assembly

java - 为什么我在运行程序时无法检索按钮?

java - 如何使按钮居中并将标题居中?

java - 在 java swing 中移动组件

java - 如何使用字符串参数(即用户输入的字符串)创建 if-else 语句?

java - 带 Swing 的 keyListener 和带 Swing/keyListener 和 Thread 的游戏关卡系统

Java JFileChooser 无法检测外语

java - Java swing 中的 JButton

java - 使 JFrame 充当对话框