JPanel positioned incorrectly

2019-01-29 14:37发布

问题:

I've made a game in Java, where you're supposed to guess a random-generated int, between 1-100. If you guess too low, a textfield will be filled with a text that says, "too low" but in Swedish. Same if you guess too high.

The problem occurs when the answer is right, I then call a method to make an Invisible JPanel, visible. That works just fine, but the Jpanel itself won't move to the top where its supposed to be. And it needs to fit perfectly since it's just an photoshopped picture of the background. The properties of the background is: 1920 x 1080. The properties of the photoshopped GG WP is 1920 x 297. The Jpane is called "grattis"

I hope I've hasn't missed something important and I appreciate all the help I can get, because Im stuck now.

Ps. Sorry for the bad english and formatting.

package slumpatal;

import java.util.Random;

//This class runs the program, the main method is here.
public class SlumpaTal extends callback1 {
    private int randomTal;
    Random random = new Random();

    @Override
    public int SetValue(int value) {
        if (value < randomTal)
            return -1;
        else if (value == randomTal)
            return 0;
        else
            return 1;

    }

    //private JFrame1 frame;
    SlumpaTal() {
        GenerateRandomNumber();
        JFrame1.createWindow(this);
    }

    public void GenerateRandomNumber() {
        randomTal = random.nextInt(100) + 1;
    }

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

.

package slumpatal;


import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;


public class JFrame1 {
    //public  static int dittSvar;
    public static void createWindow(callback1 callbackArg) {

        //Font font = new Font("Verdana", Font.BOLD,28);

        JFrame frame = new JFrame("Gissa ett Tal mellan 1-100");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel contentPane = new JPanel();
        contentPane.setOpaque(true);
        contentPane.setBorder(BorderFactory.createMatteBorder(5, 5, 5, 5, Color.WHITE));
        contentPane.setBackground(Color.WHITE);
        contentPane.setLayout(new BorderLayout(10, 10));

        ImagePanel imagePanel = new ImagePanel(callbackArg);


        //frame properties


        contentPane.add(imagePanel, BorderLayout.CENTER);
        frame.setContentPane(contentPane);
        frame.setSize(1000, 600);
        frame.setVisible(true);
    }
}


class ImagePanel extends JPanel

{
    callback1 callback;
    public int dittSvar;
    private BufferedImage image;
    JButton restartApp;
    JTextField dittSvarText1;
    JTextField dittRes1;
    BufferedImage myPicture;
    public JPanel grattis;

    public ImagePanel(callback1 callbackArg) {
        try {
            myPicture = ImageIO.read(new URL("https://imagizer.imageshack.us/v2/1375x213q90/843/r7f8.jpg"));
        } catch (Exception ex) {
            System.err.print("No reward image");
        }
        callback = callbackArg;
        setOpaque(true);
        setBorder(BorderFactory.createLineBorder(Color.BLACK, 5));
        try {
            image = ImageIO.read(new URL("http://imageshack.com/a/img835/193/v8k3.jpg"));
        }
        //If it doesn't work, write an error message, printStackTrace.
        catch (IOException e) {
            System.err.printf("%s", e.toString());
            e.printStackTrace();
        }
        createGUI();
        createGUI2();

        dittSvarText1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                dittSvar = Integer.parseInt(dittSvarText1.getText());
                int res = callback.SetValue(dittSvar);
                if (res < 0)
                    dittRes1.setText("För lågt");
                else if (res == 0) {
                    dittRes1.setText("Rätt Svar!!!!");
                    makeItVisible();
                } else
                    dittRes1.setText("För Högt");
                dittSvarText1.setText("");
            }
        });
        restartApp.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                callback.GenerateRandomNumber();
                dittSvarText1.setText(null);
                dittRes1.setText(null);
                grattis.setVisible(false);

            }
        });
    }

    public void makeItVisible() {
        System.out.print("Showing reward image");
        grattis = new JPanel();
        grattis.setOpaque(false);
        grattis.setLayout(new FlowLayout(FlowLayout.LEFT));


        if (myPicture != null) {
            JLabel picLabel = new JLabel(new ImageIcon(myPicture));

            //picLabel.setLocation(new Point(0,0));
            picLabel.setLocation(new Point(0, 0));
            // picLabel.setSize(1000,297);
            // grattis.setLocation(0, 0);
            picLabel.setVerticalAlignment(WIDTH);
            grattis.setVisible(true);
            grattis.setSize(1000, 600);

            grattis.add(picLabel);
            add(grattis);
            updateUI();
        } else {
            System.err.print("No picture created!");
        }


    }

    public void createGUI() {
        setLayout(new GridBagLayout());
        JPanel panel1 = new JPanel();

        panel1.setOpaque(false);
        panel1.setLayout(new GridLayout(2, 2, 2, 2));
        JLabel skrivdingissning = new JLabel("Skriv din gissning här : ");
        skrivdingissning.setForeground(Color.WHITE);
        dittSvarText1 = new JTextField(10);
        JLabel VadBlevDet = new JLabel("Vad blev det : ");
        VadBlevDet.setForeground(Color.WHITE);
        dittRes1 = new JTextField(10);


        panel1.add(skrivdingissning);
        panel1.add(dittSvarText1);
        panel1.add(VadBlevDet);
        panel1.add(dittRes1);

        add(panel1);

    }

    public void createGUI2() {

        JPanel panel2 = new JPanel();

        panel2.setLocation(100, 500);
        panel2.setOpaque(false);
        restartApp = new JButton("Starta om");
        restartApp.setLocation(100, 500);
        restartApp.setBackground(Color.WHITE);

        panel2.add(restartApp);

        // add(panel2);
    }


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

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        //g.drawImage(image, 0,0,this);
    }
}

abstract class callback1 {
    abstract int SetValue(int value);

    abstract void GenerateRandomNumber();

}

回答1:

I rewrote your entire project. You can translate the English into Swedish.

Here are a couple of screen shots of the game.

I used a model / view / controller pattern to code the project. This separates concerns and makes it easier to focus on one part of the project at a time.

Here are the images I used.

Here's the Package Explorer from Eclipse showing the organization of the Java Swing project.

And here's the code. I wrote one model class and 4 view classes. The controller is an anonymous class inside one of the view classes.

This is the main GuessingGame class.

package com.ggl.guessing.game;

import javax.swing.SwingUtilities;

import com.ggl.guessing.game.model.GuessingGameModel;
import com.ggl.guessing.game.view.GuessingGameFrame;

public class GuessingGame implements Runnable {

    @Override
    public void run() {
        new GuessingGameFrame(new GuessingGameModel(1, 100));

    }

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

}

Short and sweet. This class does 3 things.

  1. Puts the Swing GUI on the Event Dispatch thread (EDT) by calling the SwingUtilities invokeLater method.
  2. Instantiates the GUI model.
  3. Instantiates the GUI JFrame.

Next, lets look at the model class

package com.ggl.guessing.game.model;

import java.util.Random;

public class GuessingGameModel {

    private int lowerLimit;
    private int upperLimit;
    private int randomNumber;
    private int numberOfGuesses;


    private Random random;

    public GuessingGameModel(int lowerLimit, int upperLimit) {
        this.random = new Random();
        init(lowerLimit, upperLimit);
    }

    public void init(int lowerLimit, int upperLimit) {
        this.lowerLimit = lowerLimit;
        this.upperLimit = upperLimit;
        this.numberOfGuesses = 0;
        this.randomNumber = random.nextInt(upperLimit - lowerLimit + 1) 
                + lowerLimit;
    }

    public int guess(int guess) {
        this.numberOfGuesses++;
        if (guess < randomNumber) {
            return -1;
        } else if (guess > randomNumber) {
            return 1;
        } else {
            return 0;
        }
    }

    public int getNumberOfGuesses() {
        return numberOfGuesses;
    }

    public String getFrameTitle() {
        return "Gissa ett Tal mellan " + lowerLimit + " - "
                + upperLimit;
    }
}

This class keeps the game information, and provides a convenience method for the title. You can set the lower and upper limit on the random number, so you could add different ranges in the future.

Now, let's get to the GUI itself. This is the GuessingGameFrame class.

package com.ggl.guessing.game.view;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;

import com.ggl.guessing.game.model.GuessingGameModel;

public class GuessingGameFrame {

    private GameImages gameImages;

    private GuessingGameModel model;

    private GuessingGamePanel ggPanel;

    private JFrame frame;

    public GuessingGameFrame(GuessingGameModel model) {
        this.model = model;
        this.gameImages = new GameImages();
        createPartControl();
    }

    private void createPartControl() {
        frame = new JFrame();
        frame.setTitle(model.getFrameTitle());
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent event) {
                exitProcedure();
            }
        });

        Dimension d = sizeGuessesPanel();

        ggPanel = new GuessingGamePanel(this, model, gameImages, d);
        JPanel mainPanel = new JPanel();
        mainPanel.setLayout(new FlowLayout());
        mainPanel.add(ggPanel);

        frame.setLayout(new FlowLayout());
        frame.add(mainPanel);
//      frame.setSize(640, 480);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private Dimension sizeGuessesPanel() {
        GuessesPanel guessesPanel = new GuessesPanel(this, model);
        frame.setLayout(new FlowLayout());
        frame.add(guessesPanel.getPanel());
        frame.pack();
        Dimension d = guessesPanel.getPanel().getSize();
        guessesPanel.setPreferredSize(d);
        frame.remove(guessesPanel.getPanel());
        return d;
    }

    public void exitProcedure() {
        frame.dispose();
        System.exit(0);
    }

    public void setGuessed(boolean guessed) {
        ggPanel.setGuessed(guessed);
    }

}

Pretty standard Swing, except for the sizeGuessesPanel method. In order to place the panel in the center of the background image, I need to know how big it is. The sizeGuessesPanel uses JFrame packing to tell me the size.

The setGuessed method is a convenience method that allows me to set the boolean flag on the JPanel to draw the victory image. The controller doesn't need to know about the JPanel.

Next, we'll look at the GameImages class.

package com.ggl.guessing.game.view;

import java.awt.Dimension;
import java.awt.Image;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;

public class GameImages {

    private Image backgroundImage;
    private Image victoryImage;

    public GameImages() {
        readBackgroundImage();
        readVictoryImage();
    }

    private void readBackgroundImage() {
        Image image = null;
        try {
            URL url = getClass().getResource("/v8k3reduced.jpg");
            image = ImageIO.read(url);
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.backgroundImage = image;
    }

    private void readVictoryImage() {
        Image image = null;
        try {
            URL url = getClass().getResource("/r7f8reduced.jpg");
            image = ImageIO.read(url);
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.victoryImage = image;
    }

    public Image getBackgroundImage() {
        return backgroundImage;
    }

    public Dimension getPreferredSize() {
        return new Dimension(backgroundImage.getWidth(null),
                backgroundImage.getHeight(null));
    }

    public Image getVictoryImage() {
        return victoryImage;
    }

}

This class reads the images from an images folder that's placed in the Java build path. This ensures that the images can be read from Eclipse as well as when the images are packaged inside of a Java archive file (JAR).

Next, we'll look at the GuessingGamePanel.

package com.ggl.guessing.game.view;

import java.awt.Dimension;
import java.awt.Graphics;

import javax.swing.JPanel;

import com.ggl.guessing.game.model.GuessingGameModel;

public class GuessingGamePanel extends JPanel {

    private static final long   serialVersionUID    = 
            -2429103448910749064L;

    private boolean guessed;

    private Dimension guessesPanelDimension;

    private GameImages gameImages;

    private GuessesPanel guessesPanel;

    private GuessingGameFrame frame;

    private GuessingGameModel model;

    public GuessingGamePanel(GuessingGameFrame frame,
            GuessingGameModel model, GameImages gameImages, 
            Dimension guessesPanelDimension) {
        this.frame = frame;
        this.model = model;
        this.gameImages = gameImages;
        this.guessesPanelDimension = guessesPanelDimension;
        this.guessed = false;

        createPartControl();
    }

    private void createPartControl() {
        this.setLayout(null);
        this.setPreferredSize(gameImages.getPreferredSize());

        guessesPanel = new GuessesPanel(frame, model);
        Dimension gp = guessesPanelDimension;
        Dimension tp = gameImages.getPreferredSize();
        int x = (tp.width - gp.width) / 2;
        int y = (tp.height - gp.height) / 2;
        guessesPanel.getPanel().setBounds(x, y, gp.width, gp.height);
        this.add(guessesPanel.getPanel());
    }

    public void setGuessed(boolean guessed) {
        this.guessed = guessed;
        repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.drawImage(gameImages.getBackgroundImage(), 0, 0, null);
        if (guessed) {
            g.drawImage(gameImages.getVictoryImage(), 0, 0, null);
        }
    }
}

We extend a JPanel because we override the JPanel paintComponent method.

In the createPartControl method, you'll notice that I use a null layout. A null layout should only be used with the supervision and approval of a Swing expert. In this case, it was the only way I could control the size and position of the GuessesPanel.

Finally, we'll look at the GuessesPanel class.

package com.ggl.guessing.game.view;

import java.awt.Color;
import java.awt.Component;
import
 java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

import com.ggl.guessing.game.model.GuessingGameModel;

public class GuessesPanel {

    private static final Insets finalInsets = 
            new Insets(6, 6, 6, 6);

    private Dimension preferredSize;

    private GuessingGameFrame frame;

    private GuessingGameModel model;

    private JPanel panel;

    private JTextField dittSvarText1;
    private JTextField dittRes1;
    private JTextField guessesField;

    public GuessesPanel(GuessingGameFrame frame,
            GuessingGameModel model) {
        this.frame = frame;
        this.model = model;
        createPartControl();
    }

    private void createPartControl() {
        panel = new JPanel();
        panel.setBackground(Color.YELLOW);
        panel.setLayout(new FlowLayout());

        JPanel insidePanel = new JPanel();
        insidePanel.setLayout(new GridBagLayout());

        int gridy = 0;

        JLabel guessLabel = new JLabel("Skriv din gissning här:");
        addComponent(insidePanel, guessLabel, 0, gridy, 1, 1,
                finalInsets, GridBagConstraints.LINE_START,
                GridBagConstraints.NONE);

        dittSvarText1 = new JTextField(10);
        dittSvarText1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                int guess = Integer.valueOf(dittSvarText1.getText());
                int lowHigh = model.guess(guess);
                if (lowHigh < 0) {
                    dittRes1.setText("För lågt");
                } else if (lowHigh > 0) {
                    dittRes1.setText("För Högt");
                } else {
                    dittRes1.setText("Rätt Svar!!!!");
                    frame.setGuessed(true);
                }
                guessesField.setText(Integer.toString(
                        model.getNumberOfGuesses()));
                dittSvarText1.setSelectionStart(0);
                dittSvarText1.setSelectionEnd(
                        dittSvarText1.getText().length());
            }   
        });
        addComponent(insidePanel, dittSvarText1, 1, gridy++, 1, 1,
                finalInsets, GridBagConstraints.LINE_START,
                GridBagConstraints.NONE);

        JLabel responseLabel = new JLabel("Vad blev det:");
        addComponent(insidePanel, responseLabel, 0, gridy, 1, 1,
                finalInsets, GridBagConstraints.LINE_START,
                GridBagConstraints.NONE);

        dittRes1 = new JTextField(10);
        dittRes1.setEditable(false);
        addComponent(insidePanel, dittRes1, 1, gridy++, 1, 1,
                finalInsets, GridBagConstraints.LINE_START,
                GridBagConstraints.NONE);

        JLabel guessesLabel = new JLabel("Number of guesses:");
        addComponent(insidePanel, guessesLabel, 0, gridy, 1, 1,
                finalInsets, GridBagConstraints.LINE_START,
                GridBagConstraints.NONE);

        guessesField = new JTextField(10);
        guessesField.setEditable(false);
        addComponent(insidePanel, guessesField, 1, gridy++, 1, 1,
                finalInsets, GridBagConstraints.LINE_START,
                GridBagConstraints.NONE);

        panel.add(insidePanel);
    }

    private void addComponent(Container container, Component component,
            int gridx, int gridy, int gridwidth, int gridheight, 
            Insets insets, int anchor, int fill) {
        GridBagConstraints gbc = new GridBagConstraints(gridx, gridy,
                gridwidth, gridheight, 1.0D, 1.0D, anchor, fill, 
                insets, 0, 0);
        container.add(component, gbc);
    }

    public void setPreferredSize(Dimension preferredSize) {
        panel.setPreferredSize(preferredSize);
        panel.setMaximumSize(preferredSize);
        panel.setMinimumSize(preferredSize);
        this.preferredSize = preferredSize;
    }

    public Dimension getPreferredSize() {
        return preferredSize;
    }

    public JPanel getPanel() {
        return panel;
    }

}

This is a pretty standard JPanel that uses a GridBagLayout. The addComponent method creates a GridBagConstraints for each Swing component. A GridBagLayout is the layout to use when you want to line up labels and fields.

The main and only controller is an anonymous action listener class inside of this class.

I created more classes than you did and arranged the code differently than you did. Study the code and see if it helps you to understand MVC better, as well as how to code a Swing application.