首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java -从形状创建一个按钮

Java -从形状创建一个按钮
EN

Stack Overflow用户
提问于 2022-04-28 20:04:43
回答 1查看 55关注 0票数 0

我目前正在学习Java,并且已经被分配到完成一个程序来创建Conways的生命(我们从提供给我们的一些代码开始,并且必须添加一些特性等等)。

我目前被困在一个菜单选项的游戏。我希望它从菜单屏幕开始,在菜单屏幕顶部显示“开始”、“随机”、“加载”、“保存”按钮。我已经编写了代码,以便程序通过我的画图方法中的fillRect选项显示这些按钮。

我的问题是,如何使用mousePressed方法来识别所选的单元格,以便在它们被选中时进行操作。我已经看了很长时间了,但似乎无法做到这一点。

任何建议都是一种巨大的帮助。我在下面分享了我的代码。这是一项正在进行的工作,但在继续使用其他功能之前,我真的很想让它发挥作用。

代码语言:javascript
复制
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.*;

public class ConwaysLife extends JFrame implements Runnable, MouseListener {
    
    // member data
    private BufferStrategy strategy;
    private Graphics offscreenBuffer;
    private boolean gameState[][] = new boolean[40][40];
    private boolean isGameInProgress = false;
    
    // constructor
    public ConwaysLife () {
        //Display the window, centred on the screen
         Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
         int x = screensize.width/2 - 400;
         int y = screensize.height/2 - 400;
         setBounds(x, y, 800, 800);
         setVisible(true);
         this.setTitle("Conway's game of life");
        
         // initialise double-buffering
         createBufferStrategy(2);
         strategy = getBufferStrategy();
         offscreenBuffer = strategy.getDrawGraphics();
        
         // register the Jframe itself to receive mouse events
         addMouseListener(this);
        
         // initialise the game state
         for (x=0;x<40;x++) {
         for (y=0;y<40;y++) {
             gameState[x][y]=false;
         }
         }
         
         // create and start our animation thread
         Thread t = new Thread(this);
         t.start();
    }
    
    // thread's entry point
    public void run() {
        while ( 1==1 ) {
        // 1: sleep for 1/5 sec
            try {
                Thread.sleep(200);
        } catch (InterruptedException e) { }
            
        // 2: animate game objects [nothing yet!]
        /*if (isGameInProgress == false) {
            this.repaint();
        }*/
            
            
            
            
            
        
        // 3: force an application repaint
        this.repaint();
    }
    }
    
    
    // mouse events which must be implemented for MouseListener
     public void mousePressed(MouseEvent e) {
         
         while (!isGameInProgress) {
             int x = e.getX()/20;
             int y = e.getY()/20;
             if(x >= 10 && x <= 80 && y >= 40 && y <= 65) {
                 isGameInProgress = !isGameInProgress;
                 this.repaint();
             }
             
         }
         
    
     // determine which cell of the gameState array was clicked on
         int x = e.getX()/20;
         int y = e.getY()/20;
         // toggle the state of the cell
         gameState[x][y] = !gameState[x][y];
         // request an extra repaint, to get immediate visual feedback
         this.repaint();
     }
     
     
     public void mouseReleased(MouseEvent e) { }
     public void mouseEntered(MouseEvent e) { }
     public void mouseExited(MouseEvent e) { }
     public void mouseClicked(MouseEvent e) { }
    //
     
    
     
    // application's paint method
    public void paint(Graphics g) {
        Font font = new Font("Veranda", Font.BOLD, 20);
            
        g = offscreenBuffer; // draw to off screen buffer
        
        // clear the canvas with a big black rectangle
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, 800, 800);
        
        /*look to add a while game in progress loop here!!!*/
        // draw menu options
        if(!isGameInProgress) {
        g.setColor(Color.green); 
        g.fillRect(10, 40, 70, 25);
        g.fillRect(100, 40, 100, 25);
        g.fillRect(300, 40, 170, 25);
    
        g.setColor(Color.BLACK);
        g.setFont(font);
        g.drawString("Start", 15, 60);
        g.drawString("Random", 105, 60);
        g.drawString("Load", 305, 60);
        g.drawString("Save", 395, 60);
        }
            
        // redraw all game objects
        g.setColor(Color.WHITE);
             for (int x=0;x<40;x++) {
                 for (int y=0;y<40;y++) {
                     if (gameState[x][y]) {
                         g.fillRect(x*20, y*20, 20, 20);
                     }
                 }
             }
             
            // flip the buffers
            strategy.show();
    }
    
    // application entry point
    public static void main(String[] args) {
    ConwaysLife w = new ConwaysLife();
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-04-28 22:49:38

你不会喜欢答案,但这是解决问题的“正确”方法。

您需要了解的是,Swing/AWT使用的是“被动”呈现工作流,而BufferStrategy使用的是“主动”呈现工作流,它们互不兼容。

通常情况下,您不应该覆盖顶级容器(如paint )的JFrame,这将以无休止的问题结束。相反,您应该从类似JPanel的内容开始,而不是重写它的paintComponent方法。

尽管如此,有一个“更简单”的解决方案可供您使用。CardLayout。这将使您能够将菜单的工作流从游戏中分离出来,并解决Swing/AWT和BufferStrategy之间的问题。

例如..。

代码语言:javascript
复制
import java.awt.Canvas;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferStrategy;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public final class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new MainPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class MainPane extends JPanel {

        enum View {
            MENU, GAME;
        }

        private CardLayout cardLayout = new CardLayout();
        private GamePane gamePane;

        public MainPane() {
            setLayout(cardLayout);

            gamePane = new GamePane();

            add(new MenuPane(new MenuPane.Observer() {
                @Override
                public void startNewGame() {
                    showGame();
                }

                @Override
                public void randomGame() {
                }

                @Override
                public void loadGame() {
                }

                @Override
                public void saveGame() {
                }
            }), View.MENU);
            add(gamePane, View.GAME);
        }

        protected void add(Component compent, View view) {
            add(compent, view.name());
        }

        protected void showGame() {
            show(View.GAME);
            gamePane.start();
        }

        protected void showMenu() {
            gamePane.stop();
            show(View.MENU);
        }

        protected void show(View view) {
            cardLayout.show(this, view.name());
        }

    }

    public class MenuPane extends JPanel {

        public interface Observer {
            public void startNewGame();
            public void randomGame();
            public void loadGame();
            public void saveGame();
        }

        private Observer observer;

        public MenuPane(Observer observer) {
            this.observer = observer;

            JButton startButton = new JButton("Start");
            JButton randomButton = new JButton("Random");
            JButton loadButton = new JButton("Load");
            JButton saveButton = new JButton("Save");

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.ipadx = 10;
            gbc.ipady = 10;
            gbc.insets = new Insets(8, 8, 8, 8);
            gbc.weightx = GridBagConstraints.REMAINDER;

            add(startButton, gbc);
            add(randomButton, gbc);
            add(loadButton, gbc);
            add(saveButton, gbc);

            startButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    observer.startNewGame();
                }
            });
            randomButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    observer.randomGame();
                }
            });
            loadButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    observer.loadGame();
                }
            });
            saveButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    observer.saveGame();
                }
            });
        }

    }

    public class GamePane extends Canvas {

        private Thread thread;
        private volatile boolean isRunning = false;

        public GamePane() {
            setBackground(Color.BLACK);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(800, 800);
        }

        protected void start() {
            if (isRunning) {
                return;
            }
            createBufferStrategy(3);
            isRunning = true;
            thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    mainLoop();
                }
            });
            thread.start();
        }

        protected void stop() {
            if (!isRunning || thread == null) {
                return;
            }
            isRunning = false;
            try {
                thread.join();
            } catch (InterruptedException ex) {
            }
            thread = null;
        }

        protected void mainLoop() {
            try {
                while (isRunning) {
                    render();
                    Thread.sleep(16);
                }
            } catch (InterruptedException ex) {
            }
        }

        protected void render() {
            BufferStrategy strategy = getBufferStrategy();
            if (strategy == null) {
                return;
            }
            // Render single frame
            do {
                // The following loop ensures that the contents of the drawing buffer
                // are consistent in case the underlying surface was recreated
                do {
                    // Get a new graphics context every time through the loop
                    // to make sure the strategy is validated
                    Graphics graphics = strategy.getDrawGraphics();

                    FontMetrics fm = graphics.getFontMetrics();
                    String text = "All your game are belong to us";
                    int x = (getWidth() - fm.stringWidth(text)) / 2;
                    int y = (getHeight() - fm.getHeight()) / 2;

                    graphics.setColor(Color.WHITE);
                    graphics.drawString(text, x, y + fm.getAscent());

                    // Render to graphics
                    // ...
                    // Dispose the graphics
                    graphics.dispose();

                    // Repeat the rendering if the drawing buffer contents
                    // were restored
                } while (strategy.contentsRestored());

                // Display the buffer
                strategy.show();

                // Repeat the rendering if the drawing buffer was lost
            } while (strategy.contentsLost());
        }
    }
}

我强烈建议你花时间阅读:

基于“完全”BufferStrategy的方法..。

现在,如果您不能使用Swing,“出于原因”,则仍然可以使用“委托”实现模拟概念。

基本上,这意味着将执行某些工作流的责任“委托”给另一个工作流。在这种情况下,我们希望委派鼠标事件的呈现和处理。

这允许您有一个专门的工作流程菜单和一个专门的工作流程为游戏,而不必尝试和混合了很多状态。

为什么我要坚持分离这两个工作流呢?简单地说,这是因为它使管理和推理变得更加容易,同时也因为它支持单一责任原则

下面的示例使用Renderable interface来定义结束“呈现”委托将需要实现的核心功能,在本例中,非常简单,我们希望告诉呈现器在每次画板传递上“呈现”它的内容,我们希望(可选地)委托鼠标单击事件(这可以直接通过第二个interface甚至MouseListener interface来完成,但为了演示目的,我已经将其作为Renderable interface的一个要求。

您的实际问题的“基本”解决方案是通过使用Rectangle#contains(Point)找到的。

这基本上是检查每个“按钮”Rectangle,以确定所提供的MouseEvent是否发生在它的范围内,如果发生了,我们就采取行动。

然而,由于我们需要提前构建Rectangle,并不困难,但它实际上是依赖于父程序的状态,因为我们需要知道显示呈现程序的区域,运行这个示例,您将看到我的意思。

代码语言:javascript
复制
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;

public final class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                MainPane mainPane = new MainPane();
                JFrame frame = new JFrame();
                frame.add(mainPane);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                mainPane.start();
            }
        });
    }

    public interface Renderable {
        public void render(Graphics2D g2d, Dimension size);

        // We could just extend from MouseListener
        // but I don't need all those event handlers
        public void mouseClicked(MouseEvent e);
    }

    public class MainPane extends Canvas {

        private Thread thread;
        private volatile boolean isRunning = false;

        private Renderable currentRenderer;

        private MenuRenderer menuRenderer;
        private GameRenderer gameRenderer;

        public MainPane() {
            setBackground(Color.BLACK);

            gameRenderer = new GameRenderer();
            menuRenderer = new MenuRenderer(new MenuRenderer.Observer() {
                @Override
                public void startNewGame() {
                    showGame();
                }

                @Override
                public void randomGame() {
                }

                @Override
                public void loadGame() {
                }

                @Override
                public void saveGame() {
                }
            });

            showMenu();

            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    if (currentRenderer == null) {
                        return;
                    }
                    currentRenderer.mouseClicked(e);
                }
            });
        }

        protected void showMenu() {
            // This may need to tell the game renderer to stop
            // or pause
            currentRenderer = menuRenderer;
        }

        protected void showGame() {
            currentRenderer = gameRenderer;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(800, 800);
        }

        protected void start() {
            if (isRunning) {
                return;
            }
            createBufferStrategy(3);
            isRunning = true;
            thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    mainLoop();
                }
            });
            thread.start();
        }

        protected void stop() {
            if (!isRunning || thread == null) {
                return;
            }
            isRunning = false;
            try {
                thread.join();
            } catch (InterruptedException ex) {
            }
            thread = null;
        }

        protected void mainLoop() {
            try {
                while (isRunning) {
                    render();
                    Thread.sleep(16);
                }
            } catch (InterruptedException ex) {
            }
        }

        protected void render() {
            BufferStrategy strategy = getBufferStrategy();
            if (strategy == null && currentRenderer != null) {
                return;
            }
            // Render single frame
            do {
                // The following loop ensures that the contents of the drawing buffer
                // are consistent in case the underlying surface was recreated
                do {
                    // Get a new graphics context every time through the loop
                    // to make sure the strategy is validated
                    Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
                    g2d.setBackground(Color.BLACK);
                    g2d.fillRect(0, 0, getWidth(), getHeight());
                    RenderingHints hints = new RenderingHints(
                            RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
                    );
                    g2d.setRenderingHints(hints);
                    // Render to graphics
                    currentRenderer.render(g2d, getSize());
                    // Dispose the graphics
                    g2d.dispose();
                    // Repeat the rendering if the drawing buffer contents
                    // were restored
                } while (strategy.contentsRestored());

                // Display the buffer
                strategy.show();

                // Repeat the rendering if the drawing buffer was lost
            } while (strategy.contentsLost());
        }
    }

    public class GameRenderer implements Renderable {
        @Override
        public void render(Graphics2D g2d, Dimension size) {
            FontMetrics fm = g2d.getFontMetrics();
            String text = "All your game are belong to us";
            int x = (size.width - fm.stringWidth(text)) / 2;
            int y = (size.height - fm.getHeight()) / 2;

            g2d.setColor(Color.WHITE);
            g2d.drawString(text, x, y + fm.getAscent());
        }

        @Override
        public void mouseClicked(MouseEvent e) {
        }
    }

    public class MenuRenderer implements Renderable {

        public interface Observer {
            public void startNewGame();
            public void randomGame();
            public void loadGame();
            public void saveGame();
        }

        private Observer observer;

        private String[] menuOptions = new String[]{
            "New Game",
            "Random",
            "Load Game",
            "Save Game"
        };

        private Rectangle[] menuBounds;

        private int internalPadding = 20;
        private int horizontalGap = 16;

        public MenuRenderer(Observer observer) {
            this.observer = observer;
        }

        @Override
        public void render(Graphics2D g2d, Dimension size) {
            if (menuBounds == null) {
                createMenus(g2d, size);
            }
            renderMenus(g2d);
        }

        protected void createMenus(Graphics2D g2d, Dimension size) {
            FontMetrics fm = g2d.getFontMetrics();
            int totalHeight = (((fm.getHeight() + internalPadding) + horizontalGap) * menuOptions.length) - horizontalGap;
            int buttonHeight = fm.getHeight() + internalPadding;

            menuBounds = new Rectangle[menuOptions.length];

            int buttonWidth = 0;
            for (int index = 0; index < menuOptions.length; index++) {
                int width = fm.stringWidth(menuOptions[index]) + internalPadding;
                buttonWidth = Math.max(width, buttonWidth);
            }

            int yPos = (size.height - totalHeight) / 2;
            for (int index = 0; index < menuOptions.length; index++) {
                int xPos = (size.width - buttonWidth) / 2;
                Rectangle menuRectangle = new Rectangle(xPos, yPos, buttonWidth, buttonHeight);
                menuBounds[index] = menuRectangle;
                yPos += buttonHeight + (horizontalGap / 2);
            }
        }

        protected void renderMenus(Graphics2D g2d) {
            for (int index = 0; index < menuOptions.length; index++) {
                String text = menuOptions[index];
                Rectangle bounds = menuBounds[index];
                renderMenu(g2d, text, bounds);
            }
        }

        protected void renderMenu(Graphics2D g2d, String text, Rectangle bounds) {
            FontMetrics fm = g2d.getFontMetrics();
            int textWidth = fm.stringWidth(text);

            int textXPos = (bounds.x + (internalPadding / 2)) + ((bounds.width - internalPadding - textWidth) / 2);
            int textYPos = bounds.y + (internalPadding / 2);

            RoundRectangle2D buttonBackground = new RoundRectangle2D.Double(bounds.x, bounds.y, bounds.width, bounds.height, 20, 20);

            g2d.setColor(Color.BLUE.darker());
            g2d.fill(buttonBackground);

            g2d.setColor(Color.WHITE);
            g2d.drawString(text, textXPos, textYPos + fm.getAscent());
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            if (menuBounds == null) {
                return;
            }
            for (int index = 0; index < menuOptions.length; index++) {
                if (menuBounds[index].contains(e.getPoint())) {
                    switch (index) {
                        case 0:
                            observer.startNewGame();
                            break;
                        case 2:
                            observer.randomGame();
                            break;
                        case 3:
                            observer.loadGame();
                            break;
                        case 4:
                            observer.saveGame();
                            break;
                    }
                }
            }
        }
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72049597

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档