how to adjust image in a panel in Java swing?

2019-07-17 04:29发布

问题:

I have some bits of code with a lot of help from fellow programmers in SO. First off, Thank you all for that. Now I have this code for hangman. I want the textfield to appear below the image, right now it is shooting on top of it. I have provided the link for the image in the code. but you might need to download it to avoid exception thrown. I want to keep top right corner free for timer, which I have not thought how to do yet. I need assistance in getting image and texfield in correct positions. please execute code to see how it looks currently. I tried both BorderLayout.SOUTH and BorderLayout.PAGE_END, but it did not help Thank you

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.text.MaskFormatter;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;

public class HangmanGUI {
   private DetailsPanel myPanel;
   private ImagePanel imagePanel = new ImagePanel();

   public HangmanGUI() throws ParseException {
      myPanel = new DetailsPanel();
      JFrame myframe = new JFrame();
      // myframe.getContentPane().setLayout(new BorderLayout());
      myframe.getContentPane().add(imagePanel, BorderLayout.CENTER);
      myframe.getContentPane().add(myPanel, BorderLayout.SOUTH);
      myframe.setTitle("Hangman Game");
      // myframe.setVisible(true);
      // myframe.setLocationRelativeTo(null);
      myframe.pack();
      myframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      myframe.setLocationRelativeTo(null);
      myframe.setVisible(true);
   }

   public static void main(String[] args) throws ParseException {
      new HangmanGUI();
   }
}

class ImagePanel extends JPanel {
   private static final int PREF_W = 400;
   private static final int PREF_H = PREF_W;
   private static final String TITLE = "Hangman Image";
   private BufferedImage image;

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

   public ImagePanel() {
      setBorder(BorderFactory.createTitledBorder(TITLE));
      try {
          image = ImageIO.read(new File("http://upload.wikimedia.org/wikipedia/commons/8/8b/Hangman-0.png"));
      } catch (IOException ex) {
           ex.printStackTrace();
      }

      add(createFormattedPanel(),BorderLayout.SOUTH);
  //add(createFormattedPanel(),BorderLayout.PAGE_END);
   }

   @Override
   protected void paintComponent(Graphics g) {
       super.paintComponent(g);
       g.drawImage(image, 0, 0, null); // see javadoc for more info on the parameters            
   }

   public JPanel createFormattedPanel() {
          JPanel panel = new JPanel();
          MaskFormatter formatter = null;
          try {
             JLabel label = new JLabel("Guesss");
             formatter = new MaskFormatter("? ? ? ? ? ? ?");
             formatter.setPlaceholderCharacter('?');
             JFormattedTextField input = new JFormattedTextField(formatter);
             input.setColumns(20);
             panel.add(label);
             panel.add(input);
          } catch (java.text.ParseException exc) {
             System.err.println("formatter is bad: " + exc.getMessage());
             System.exit(-1);
          }

          return panel;
       }

   }

class DetailsPanel extends JPanel {
   public DetailsPanel() {
      setLayout(new BorderLayout());

      setBorder(BorderFactory.createTitledBorder(" click here "));
      //add(createFormattedPanel(), BorderLayout.PAGE_START);

      JPanel letterPanel = new JPanel(new GridLayout(0, 5));
      for (char alphabet = 'A'; alphabet <= 'Z'; alphabet++) {
         String buttonText = String.valueOf(alphabet);
         JButton letterButton = new JButton(buttonText);
         letterButton.addActionListener(clickedbutton());
         letterPanel.add(letterButton, BorderLayout.CENTER);
      }
      add(letterPanel, BorderLayout.CENTER);
   }

   private ActionListener clickedbutton() {
      return new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            String actionCommand = e.getActionCommand();
            System.out.println("actionCommand is: " + actionCommand);
         }
      };
   }

}

回答1:

Because you're mixing custom painting and components into a single component, you are losing control over how your components are laid out.

Two solutions come to mind...

Use a JLabel

Use a JLabel to display the image, placing it in the CENTER position of the ImagePane and the "formatted panel" in the SOUTH position

Separate your components in logic groups

Split apart your UI so that each component is isolated...

-- Hangman Image --------------
| --------------------------- |
| | ----------------------- | |
| | |                     | | |
| | |                     | | |
| | | (Image Pane)        | | |
| | |                     | | |
| | |                     | | |
| | ----------------------- | |
| | ----------------------- | |
| | | Guess: ? ? ? ? ? ?  | | |
| | ----------------------- | |
| --------------------------- |
| -- click here ------------- |
| |                         | |
| |                         | |
| | (buttons   )            | |
| |                         | |
| |                         | |
| --------------------------- |
-------------------------------

Basically, the ImagePane, the GuessPane and the ClickPane are all separate components.

You would then add the ImagePane and GuessPane onto another JPanel (using BorderLayout) and then add this and the ClickPane onto the main panel (using a BorderLayout).

This will give you greater control over how the individual components are laid out and can be combined.

== Example Using JLabel ==

This is a basic example of using a JLabel over using custom painting.

The ImagePane and GuessPane are added to separate panel, which is added to the CENTER position of the frame. The DetailsPane is then added to the SOUTH position of the frame...

import javax.swing.*;
import javax.swing.text.MaskFormatter;

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.text.ParseException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

public class HangmanGUI {

    private DetailsPanel myPanel;

    public HangmanGUI() throws ParseException {
        myPanel = new DetailsPanel();
        JFrame myframe = new JFrame();

        JPanel content = new JPanel(new BorderLayout());
        content.add(new ImagePane());
        content.add(new GuessPane(), BorderLayout.SOUTH);
        content.setBorder(BorderFactory.createTitledBorder("Hangman Image"));

        myframe.getContentPane().add(content, BorderLayout.CENTER);
        myframe.getContentPane().add(myPanel, BorderLayout.SOUTH);
        myframe.setTitle("Hangman Game");
        myframe.pack();
        myframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        myframe.setLocationRelativeTo(null);
        myframe.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }
                try {
                    new HangmanGUI();
                } catch (ParseException ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    public class ImagePane extends JPanel {

        private JLabel label;

        public ImagePane() {
            setLayout(new BorderLayout());
            label = new JLabel();
            add(label);
            try {
                label.setIcon(new ImageIcon(ImageIO.read(new URL("http://upload.wikimedia.org/wikipedia/commons/8/8b/Hangman-0.png"))));
            } catch (IOException ex) {
                label.setText("Bad Image");
                ex.printStackTrace();
            }
        }

    }

    public static class GuessPane extends JPanel {

        public GuessPane() {
            MaskFormatter formatter = null;
            try {
                JLabel label = new JLabel("Guesss");
                formatter = new MaskFormatter("? ? ? ? ? ? ?");
                formatter.setPlaceholderCharacter('?');
                JFormattedTextField input = new JFormattedTextField(formatter);
                input.setColumns(20);
                add(label);
                add(input);
            } catch (java.text.ParseException exc) {
                System.err.println("formatter is bad: " + exc.getMessage());
                System.exit(-1);
            }
        }

    }

    public static class DetailsPanel extends JPanel {

        public DetailsPanel() {
            setLayout(new BorderLayout());

            setBorder(BorderFactory.createTitledBorder(" click here "));
            //add(createFormattedPanel(), BorderLayout.PAGE_START);

            JPanel letterPanel = new JPanel(new GridLayout(0, 5));
            for (char alphabet = 'A'; alphabet <= 'Z'; alphabet++) {
                String buttonText = String.valueOf(alphabet);
                JButton letterButton = new JButton(buttonText);
                letterButton.addActionListener(clickedbutton());
                letterPanel.add(letterButton, BorderLayout.CENTER);
            }
            add(letterPanel, BorderLayout.CENTER);
        }

        private ActionListener clickedbutton() {
            return new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    String actionCommand = e.getActionCommand();
                    System.out.println("actionCommand is: " + actionCommand);
                }
            };
        }

    }
}

This type of separation is going to require to use some kind of model to bridge across the components.

That is, when the user makes a guess, you need to update the model, which will notify the other components (via some kind of listener API) that some change has occurred, this will allow the other UI elements to update themselves as required to meet the requirements of the model.

The model should contain the logic for the game and drive the UI.