Can not complete drawline on JButton [closed]

2019-09-20 23:41发布

So I am making a simple tic tac toe game and ran into a problem at the last minute

I am trying to draw a line at the win location but on the final win location(index), the line gets hidden behind the JButton not entirly sure why it is doing this.

I know alot of people say do not use getGraphics(), and I am wondering if that is the source of my issues they say to override the paintComponent method but that is not working for me either

I have attached a pic of what the result is looking like and code snips of how I am trying to perform these actions

PS I am using a JFrame, if any more code is needed I will be glad to show it

enter image description here

if(win[i] == 264){ // if one of the the combinations equal 'X','X','X' which equals 264, then there is a winner 
            System.out.println("X is the winner!!!");   
            System.out.println("Game Over!");
            number = i;
            draw(); }// call draw method 

 private void draw(){   // drawing a line at winning location 


Graphics2D  g1 = (Graphics2D) GUI.getFrame().getGraphics(); // declaring graphics on our Jframe 
    Stroke stroke3 = new BasicStroke(12f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER); // make our strokes cap off round 

    if(number == 0){ // statements will determine the win location, so at win0, XXX, 
        g1.setStroke(stroke3); // we will add stroke to our line 
        g1.drawLine(0,104,500,104); // draw the line starting at the 0,104 and end it at coordinates 500,104 
    }

here is a more runnable code, it is alot though

    import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;


public class tic implements Runnable {
    final static int row = 3; // our rows 
    final static int col = 3; // our col 
    final static int sizeOfBoard = row * col; 
    // the size of our board is not going to change so we make it final
    static JButton[] clickButton;
    char[] templateOfBoard; // our board, TicTacToe field, static
    char userTurn; // users turn , only one letter, tracks whether it is a X or O 
    int count; // keeps track of user moves
    static JFrame frame; // our JFrame
    int number;


    public tic(JFrame frame) {

        tic.frame = new JFrame("TicTacToe GAME");  
        clickButton = new JButton[9];
        count = 0; // number of turns starts at 0;
        number = 0;

        setUserTurn('X'); // first turn will always be X
        setTemplateOfBoard(new char[sizeOfBoard]); // size of the board we are going to make it 


        try{
            for(int spaces=0; spaces<sizeOfBoard; spaces++){ // size of Board is in the GUI class

                getTemplateOfBoard()[spaces] = ' '; // the board is being created, looping through all rows and col

                //every index of the board not has a char value equal to a space 

                //determine if everything came out correctly 
                //should equal of a total of 9
                // 3x3
            }
            System.out.println("Board template created"); // means the board now has all spaces 
        }
        catch(Exception e){
            System.out.println("Could not initalize the board to empty char");
            e.printStackTrace();
        }
    }

    public static void main(String[] args){

        try{
            SwingUtilities.invokeLater(new tic(frame)); // run 
        }
        catch(Exception e){ // wanted to test to ensure that Runnable could be invoked 
            System.out.println("Could not excute Runnable application");
            e.printStackTrace();
        }
    }

    public void run()  {

        setup(); // going to run out setup method, what our game is made out of 
    } 

    public void setup() {
        // setting up the Board 
        // board is composed of JButton 
        // and a 3x3 frame 


        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // when the user closes the window JFrame  will exit 

        //going to design the board now 
        //the dimensations of the board = sizeOfBoard

        getFrame().setLayout(new GridLayout(row, col)); // this is the outline rows * col 
        // sizes out row * col based on what we define those numbers as
        //i.e 3x3

        getFrame().setBounds(0,0,500,500); // location at 0,0, size 500 x 500 
        Border border = new LineBorder(Color.DARK_GRAY, 2); // color of JButton border 

        System.out.println("Your board game is being created!");

        try{
            getFrame().setVisible(true); // shows the board, 
            // this is going to display everything to the screen 
            System.out.println("Board is now visable");
        }
        catch(Exception e){
            System.out.println("Board was not displayed");
        }

        // 9 different buttons, for every index there will be a button 
        for(int i =0; i<sizeOfBoard;i++){ // going to fill the board with clickableButtons by looping through every index and placing a button there 
            final int move = i;

            clickButton[i] = new JButton(); // at a certain index there is a new button 
            clickButton[i].setSize(250,250); // size of each button 
            clickButton[i].setBackground(Color.WHITE); // color of the JButton 

            getFrame().add(clickButton[i]); // we are going to add the actual the button at that index on the frame 

            clickButton[i].setFont(new Font("Arial", Font.BOLD, 70)); // size of the text 
            clickButton[i].setBorder(border); // adding border 

            clickButton[i].getModel().addChangeListener(new ChangeListener() { //going to overRide what happens when we rollover and press a Butotn 

                public void stateChanged(ChangeEvent e) {
                    ButtonModel button = (ButtonModel) e.getSource(); // manages the state of the button, i.e lets me control what happens to the button 

                    if(clickButton[move] != null){ // if we do not include this argument 
                        // the buttons are not made yet on the new game, meaning clickButton[i] = null
                        //so boolean(!button.isRollover()) will return true, since on the new game you can not have your mouse hovered over 
                        // but when it returns true, it will return a null value, giving a null pointer exception 
                        // so best thing to do, is to only run these cases below when the buttons are not null

                        if (button.isRollover()) { // when the mouse hovers over the index 
                            clickButton[move].setBackground(Color.BLACK); // color will equal black 
                        }
                        else if(!button.isRollover()){ // when the button is not hovered over
                            clickButton[move].setBackground(Color.WHITE); // color will be whte, just like our background 
                        }

                    }
                }
            });


            clickButton[i].addActionListener(new ActionListener() { 

                //our click events, going to override to let it know what we want to happen
                //once we click on the button
                public void actionPerformed(ActionEvent e) {


                    clickButton[move].setEnabled(false); //going to disable the button after it is clicked 
                    //ORDER: button gets clicked first, then the test is added
                    mouseListener(e, move); // our mouseListenerEvent in game class 

                    //
                } 
            });
        }           
    }


    public static void playAgain() {

        try{
            System.out.println("NEW GAME");
            SwingUtilities.invokeLater(new tic(frame));  // run the run(class) again 
        }
        catch(Exception e){ // wanted to test to ensure that Runnable could be invoked 
            System.out.println("Could not excute Runnable application");
            e.printStackTrace();
        }
    }

    public static JFrame getFrame() {
        return frame;
    }

    public tic userMove(int moveMade){

        getTemplateOfBoard()[moveMade] = getUserTurn();
        // index of the board, or in simpler terms, where the user
        // inserts there turn i.e X or O, 0-8
        //System.out.println(userMove);

        //boolean statement to determine the turns 
        // So user X starts first
        //if the turn is X, the nextTurn is now O,

        if(getUserTurn() == 'X'){
            setUserTurn('O');   
        }
        else {
            setUserTurn('X');
        }

        count++; 

        return this; // going to return the userTurn
        // issue actually entering the userTurn is not giving right value, but using 'this' does 
    }


    // for some odd reason the toString is causing some issues, keep getting @hash code
    //saw online to override it like this 
    // will make the board out of emepty strings 
    // going to return a string representation of an object 
    public String toString(){
        return new String(getTemplateOfBoard());
    }

    public void mouseListener(ActionEvent e, int moveMade){ 
        // mouse click events 
        // what happens after a button is clicked 

        if(getTemplateOfBoard()[moveMade] == ' '){ // the user can only space a click, so an letter on the field if it is empty 
            ((JButton)e.getSource()).setText(Character.toString(getUserTurn())); // when the button is clicked, we want an X placed there 
            if (getUserTurn() == 'X'){
                UIManager.getDefaults().put("Button.disabledText",Color.RED); // when the but gets disabled the test will turn red      
            }
            else{
                UIManager.getDefaults().put("Button.disabledText",Color.BLUE);
            }
            //calling the method userTurn to determine who goes next
            //problem is that is expects a String
            //going to override the toString method 

            userMove(moveMade); // calling userMove in moveMade, moveMade is the index at which the user put either an X or a O
            winner(); // we want to check each time to ensure there was/was not a winner 

        } 

    }


    public tic winner() { // determines who is the winner


        //list below defines all the possible win combinations 
        // the index of where a X or O can be place
        // placed the locations to a int value 
        int win1 = templateOfBoard[0] + templateOfBoard[1] + templateOfBoard[2];    
        int win2 = templateOfBoard[3] + templateOfBoard[4] + templateOfBoard[5];
        int win3 = templateOfBoard[6] + templateOfBoard[7] + templateOfBoard[8];
        int win4 = templateOfBoard[0] + templateOfBoard[3] + templateOfBoard[6];
        int win5 = templateOfBoard[1] + templateOfBoard[4] + templateOfBoard[7];
        int win6 = templateOfBoard[2] + templateOfBoard[5] + templateOfBoard[8];
        int win7 = templateOfBoard[0] + templateOfBoard[4] + templateOfBoard[8];
        int win8 = templateOfBoard[2] + templateOfBoard[4] + templateOfBoard[6];

        int[] win = new int[]{win1,win2,win3,win4,win5,win6,win7,win8}; 
        // making a array to go through all the possibile wins 
        //possible total of wins is 8


        for(int i = 0;i<win.length;i++){     
            // looping through the win possibilities 


            if(win[i] == 264){ // if one of the the combinations equal 'X','X','X' which equals 264, then there is a winner 
                System.out.println("X is the winner!!!");   
                System.out.println("Game Over!");
                number = i;
                draw(); // call draw method 



                return this; // if statement is true, it will return this(gameOver)
            }
            else if(win[i] == 237 ){ // if one of the the combinations equal 'O','O','O' which equals 234, then there is a winner 
                System.out.println("O is the winner!!!");
                System.out.println("Game Over!");
                number = i;
                //draw(); // call draw method 




                return this;

            }

            if (count == 9) {
                // if none of the statements above are true, it automatically comes done to here
                //so if there is nine moves and no win, it is a draw 


            } 

        }

        return this; 
        // going to return this method ;
    } 

    private void draw(){    // drawing a line at winning location 


        Graphics2D  g1 = (Graphics2D) getFrame().getGraphics(); // declaring graphics on our Jframe 
        Stroke stroke3 = new BasicStroke(12f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER); // make our strokes cap off round 

        if(number == 0){ // statements will determine the win location, so at win0, XXX, 
            g1.setStroke(stroke3); // we will add stroke to our line 
            g1.drawLine(0,104,500,104); // draw the line starting at the 0,104 and end it at coordinates 500,104 
        }
        else if(number == 1){
            g1.setStroke(stroke3);
            g1.drawLine(0,257,500,257);
        }
        else if(number == 2){
            g1.setStroke(stroke3);
            g1.drawLine(0,411,500,411); 
        }
        else if(number == 3){
            g1.setStroke(stroke3);
            g1.drawLine(88,0,88,500);   
        }
        else if(number == 4){
            g1.setStroke(stroke3);
            g1.drawLine(250,0,250,500);
        }
        else if(number == 5){
            g1.setStroke(stroke3);
            g1.drawLine(411,0,411,500);
        }
        else if(number == 6){
            g1.setStroke(stroke3);
            g1.drawLine(-22,0,500,500);

        }
        else if(number == 7){
            g1.setStroke(stroke3);
            g1.drawLine(520,0,0,500);
        }
    }

    // want to be able to access the private variables 
    //so we will make getter and setter methods for the ones that we need
    public char getUserTurn() { // getter method for userTurn
        return userTurn;
    }

    public void setUserTurn(char userTurn) { // setter method 
        this.userTurn = userTurn;
    }

    public char[] getTemplateOfBoard() { //getter method 
        return templateOfBoard;
    }

    public void setTemplateOfBoard(char[] templateOfBoard) { // setter method 
        this.templateOfBoard = templateOfBoard;
    }

}

1条回答
再贱就再见
2楼-- · 2019-09-21 00:33

Painting over the top of components can be troublesome, you can't override the paintComponent method of the container which contains the components, because this paints in the background, you can't override the paint method of the container, as child components can be painted without the parent container been notified...

You could add a transparent component over the whole lot, but this just introduces more complexity, especially when a component already already exists ...

Connect the dots

public class ConnectTheDots {

    public static void main(String[] args) {
        new ConnectTheDots();
    }

    public ConnectTheDots() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }

                PaintPane pp = new PaintPane();

                JFrame frame = new JFrame("Test");
                frame.setGlassPane(pp);
                pp.setVisible(true);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new DotsPane(pp));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class PaintPane extends JPanel {

        private List<JButton[]> connections;
        private JButton lastSelected;

        public PaintPane() {
            setOpaque(false);
            connections = new ArrayList<>(25);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            if (lastSelected != null) {
                g2d.setColor(Color.RED);
                int x = lastSelected.getX() + ((lastSelected.getWidth() - 8) / 2);
                int y = lastSelected.getY() + ((lastSelected.getHeight() - 8) / 2);
                g2d.fillOval(x, y, 8, 8);
            }
            for (JButton[] group : connections) {
                g2d.setColor(Color.BLUE);
                Point startPoint = group[0].getLocation();
                Point endPoint = group[1].getLocation();
                startPoint.x += (group[0].getWidth() / 2);
                startPoint.y += (group[1].getHeight()/ 2);
                endPoint.x += (group[0].getWidth() / 2);
                endPoint.y += (group[1].getHeight()/ 2);
                g2d.draw(new Line2D.Float(startPoint, endPoint));
            }
            g2d.dispose();
        }

        protected void buttonClicked(JButton btn) {
            if (lastSelected == null) {
                lastSelected = btn;
            } else {
                connections.add(new JButton[]{lastSelected, btn});
                lastSelected = null;
            }
            revalidate();
            repaint();
        }
    }

    public class DotsPane extends JPanel {

        private PaintPane paintPane;

        public DotsPane(final PaintPane pp) {
            paintPane = pp;
            ActionListener al = new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    JButton btn = (JButton) e.getSource();
                    paintPane.buttonClicked(btn);
                }
            };
            setLayout(new GridLayout(6, 6));
            for (int index = 0; index < 6 * 6; index++) {
                JButton btn = new JButton(".");
                add(btn);
                btn.addActionListener(al);
            }
        }

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

Take a look at How to Use Root Panes for more details

查看更多
登录 后发表回答