I have the following code to create my GUI.
private static void createGUI() {
JFrame frame = new JFrame ("Tiles Game");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.setJMenuBar (new JMenuBar());
frame.setContentPane (MainPanel.getInstance());
frame.pack();
frame.setResizable (false);
frame.setLocationRelativeTo (null);
frame.setVisible (true);
}
This is the MainPanel (extends JPanel) constructor:
private MainPanel() {
super (new BorderLayout());
setPreferredSize (new Dimension (IMG_SIZE + 10, IMG_SIZE + 10));
...
panel = new ImagePanel();
add (panel, BorderLayout.CENTER);
}
And this is the ImagePanel (extends JPanel) constructor:
private ImagePanel() {
super();
setPreferredSize (new Dimension (IMG_SIZE, IMG_SIZE));
...
}
However the ImagePanel is aligned to the top left corner of the MainPanel rather than centered, so I get a bunch of extra padding at the bottom and right sides, and none at the top and left sides. How do I place it at the center of the MainPanel?
Probably what's happening is that you are drawing your image from (0, 0) which are the top-left corner. Then you set the preferred size to 10 pixels larger which makes the panel larger, but the image is still at (0, 0)
Instead use the same size witoout adding 10, and just use an EmptyBorder for the panel. Also as a recommendation, override getPreferredSize() instead of using setPreferredSize()
public class ImagePanel extends JPanel {
public ImagePanel() {
setBorder(new EmptyBorder(10, 10, 10, 10));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(IMG_SIZE, IMG_SIZE);
}
}
Also you may want to consider using a GridBagLayout for the container panel, for a sure center, if the container is to be larger than the the child panel. Few things you could do. Even consider using a ImageIcon and JLabel instead of painting (if the image doesn't need to be resized (as Camickr(+1) pointed out). A JLabel could easily be made a background by just setting the layout of the label and set it as the content pane of the frame.
ImageIcon icon = new ImageIcon(...)
JLabel frameBackground = new JLabel(icon);
frameBackground.setLayout(new BorderLayout());
frame.setContentPane(frameBackground);
Don't use a JPanel to display an image.
Instead use a JLabel with an ImageIcon.
If you want extra space around the image then you use an EmptyBorder on the label.
Related
So basically when I add a button it essentially pushes the black rectangle drawn in this program down, putting it out of its given location. How would you fix this?
import javax.swing.*;
import java.awt.*;
public class Grid {
public class homeGraphics extends JComponent {
homeGraphics() {
setPreferredSize(new Dimension(450, 600));
}
public void paint(Graphics g) {
super.paint(g);
g.fillRect(200, 275, 50, 50);
}
}
public void homeFrame() {
JFrame frame1 = new JFrame();
frame1.setSize(450, 600);
frame1.setResizable(false);
frame1.setDefaultCloseOperation(frame1.EXIT_ON_CLOSE);
JButton playButton = new JButton("Play");
playButton.setPreferredSize(new Dimension(60, 30));
JPanel panel1 = new JPanel();
panel1.add(playButton);
panel1.add(new homeGraphics());
frame1.add(panel1);
frame1.setVisible(true);
}
public static void main(String args[]) {
Grid frame = new Grid();
frame.homeFrame();
}
}```
it essentially pushes the black rectangle drawn in this program down, putting it out of its given location.
What do you mean out of its location? Painting is always done relative to the component. So your painting will always be done at (200, 275) of the component.
If you are attempting to paint at (200, 275) relative to the "frame", then don't. That is NOT how painting works.
Other problems with your code:
Don't attempt to set the size of your frame. If the custom panel is (450, 600) how can the frame possibly be the same size? The frame also contains the "title bar" and "borders". Instead of using setSize(), you invoke frame.pack()just beforeframe1.setVisible(….)`.
Class names start with an upper case character. Learn by example. Have you ever seen a class name in the JDK that doesn't start with an upper case character?
Custom painting is done by overriding paintComponent(…), not paint().
By default a JPanel uses a FlowLayout. So what you see it the button on one line and then the "HomeGraphics" class is too big to fit on the same line so it wraps the to the second line.
You should be more explicit when you do frame layout. So your code should be something like:
JPanel wrapper = new JPanel();
wrapper.add( playButton );
//JPanel panel1 = new JPanel();
//panel1.add(playButton);
//panel1.add(new homeGraphics());
JPanel panel1 = new JPanel( new BorderLayout() );
panel1.add(wrapper, BorderLayout.PAGE_START);
panel1.add(new HomeGraphics(), BorderLayout.CENTER);
Now the code shows your layout attempt more clearly.
I have the following code:
public JPanel getPanel() {
if(jpanel == null) {
jpanel = new JPanel();
jpanel.setLayout(new FlowLayout());
jpanel.setBounds(x, y, width, height);
jpanel.setBackground(Color.WHITE);
JLabel tituloLbl = new JLabel(titulo);
JLabel cantidadLbl = new JLabel(""+cantidad);
JLabel abejasLbl = new JLabel("Abejas");
//tituloLbl.setBounds(0, 0, 50, 15);
jpanel.add(tituloLbl);
jpanel.add(cantidadLbl);
jpanel.add(abejasLbl);
}
return jpanel;
}
The panel should look like a small white box with 3 labels in it, however, the labels don't show unless I set their bounds. Why does this happen? If I'm setting a FlowLayout, the labels should be positioned automatically.
This is how the panel shows:
Solved it, I had to call panel.validate() on my main frame in order to show them.
How to resize the inputpanel and gamepanel so it can take the whole frame? the inputpanel.setsize() wont work and the other problem is that the gamepanel wont display its background image.
what I did is I added a mainpanel to the frame that contains the other 2 panel the inputpanel and the gamepanel. Both panels displays with their borders but the size is very small and I needed them to fit the screen
public class GameMaster {
JFrame frame = new JFrame();
JPanel gamepanel = new Gamepanel();
JPanel mainpanel = new JPanel();
JPanel inputpanel = new JPanel();
JButton button1 = new JButton("Hoy!");
public GameMaster(){
frame.setTitle("gayEdward vs Charlie's angels");
frame.setSize(800,600);
frame.setVisible(true);
frame.setDefaultCloseOperation(3);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setLayout(new FlowLayout());
gamepanel.setSize(600, 600);
inputpanel.setSize(200, 600);
mainpanel.setSize(800, 600);
gamepanel.setBorder(BorderFactory.createLineBorder(Color.black));
inputpanel.setBorder(BorderFactory.createLineBorder(Color.black));
mainpanel.setLayout(new FlowLayout());
mainpanel.add(gamepanel);
mainpanel.add(inputpanel);
frame.add(mainpanel);
}
This is the Gamepanel class where The Gamepanel should set the image as its background
public class Gamepanel extends JPanel {
Image image;
public Gamepanel(){
try
{
image = ImageIO.read(getClass().getResourceAsStream("C:\\Users\\phg00159\\workspace\\Mp2\\pvzbg.jpg"));;
}
catch (Exception e) { /*handled in paintComponent()*/ }
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
if (image != null)
g.drawImage(image, 0,0,this.getWidth(),this.getHeight(),this);
}
}
You need to override getPreferredSize() in the GamePanel class. You should always do this when doing custom painting. The panel has no preferred size because it has no components. And the layout you are using FlowLayout takes the preferred size into consideration. Then just pack() the frame, instead of setting the size. Also note that setSize() for components is generally used for null layouts (which I strongly am against). With layout managers, they are determine by preferredSize
public class GamePanel extends JPanel {
private Image image;
#Override
public Dimension getPreferredSize() {
return new Dimension(image.getWidth(this), image.getHeight(this));
// or whatever you want the size to be
}
}
Aside:
/*handled in paintComponent()*/ - A null check isn't a substitute for an empty catch block.
frame.setVisible() should the last thing you do after adding all your components
UPDATE
As #MadProgrammer pointed out to me, you want the panel to resize with the frame. In order to do that, you need to use a different layout manager. The two that won't respect the preferred size of your panels will be either GridLayout or BorderLayout. These will "stretch your components to fit". So choose one of them, or a combination of both, seeing as you have nested panels. But you should also keep the overriden getPreferredSize() and still pack() your frame This will give you the initial size of the frame, based on your preferred size
I am creating a game which has a background image with cards displayed overtop. I would like to place the background image and cards such that they're always centered vertically and horizontally, even upon resizing the JFrame.
Currently, I am creating the cards (each a JPanel) and adding them into a container JPanel (no layout manager), then I add that Jpanel to the JFrame. After that I place the background image in a JPanel, then add that JPanel to the JFrame. The result is: The background image is hidden behind the cards and revealed when removing each card as desired. The background image is always centered but the card's JPanel does not move around upon resize. I am having a hard time getting the cards to always be centered, no matter what I try. I also need to add another JPanel to the JFrame in the South border, so that will need to work as well. I appreciate your assistance!
In the class that extends JFrame:
setSize(1060,700);
cardPanel = new JPanel();
cardPanel.setSize(1060,700);
cardPanel.setOpaque(false);
cardPanel.setLayout(null);
...card.setLocation(x, y); //loop through cards
...cardPanel.add(card); //and add each one
add(cardPanel, BorderLayout.CENTER); //add cardPanel to JFrame
//Add background image
bgPanel = new JPanel();
URL url = getClass().getResource("images/dragon_bg.png");
imgIcon = new ImageIcon(url);
JLabel background = new JLabel(imgIcon);
bgPanel.setLayout(new BorderLayout());
bgPanel.add(background);
add(bgPanel, BorderLayout.CENTER);
setVisible(true);
I would like to place the background image and cards such that they're always centered vertically and horizontally, even upon resizing the JFrame.
Then you need to use layout managers on your panels. The layout manager is responsible for redoing the layout.
How to add two JPanels to a JFrame in the center?
You could try using the OverlayLayout for this. I think the basic code would be:
JPanel contentPane = new JPanel( new GrigBagLayo9ut() );
frame.add(contentPane, BorderLayout.CENTER);
JPanel overlay = new JPanel()
overlay.setLayout( new OverlayLayout(overlay) );
contentPane.add(overlay, new GridBagConstraints()); // this should center the overlay panel
overlay.add(yourCardPanel); // you care panel must use a suitable layout
overlay.add(new JLabel() ); // use a JLabel for the background not a custom panel
I also need to add another JPanel to the JFrame in the South border,
The default layout manager for a JFrame's content pane is a BorderLayout. We already added the game panel to the center, so know you just add your other panel to the SOUTH.
If the OverlayLayout doesn't work the way you want then you will need to nest panels. Something like:
JPanel center = new JPanel( new GridBagLayout() );
frame.add(center, BorderLayout.CENTER);
JLabel background = new JLabel(...);
background.setLayoutManager( new GridBagLayout() );
center.add(background, new GridBagConstraints());
background.add(yourCardPanel, new GridBagConstraints());
Edit:
Using nested panels:
import java.awt.*;
import javax.swing.*;
public class GridBagLayoutCenter extends JPanel
{
public GridBagLayoutCenter()
{
setLayout( new BorderLayout() );
JLabel background = new JLabel( new ImageIcon("mong.jpg") );
background.setLayout( new GridBagLayout() );
add(background, BorderLayout.CENTER);
JPanel tiles = new JPanel();
tiles.setPreferredSize( new Dimension(200, 200) );
tiles.setBackground( Color.RED );
background.add(tiles, new GridBagConstraints());
add(new JLabel("SOUTH"), BorderLayout.SOUTH);
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("GridBagLayoutCenter");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new GridBagLayoutCenter() );
frame.setSize(400, 400);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
The preferred size of the "tiles" panel should not be hardcoded. The size should be determined by your custom layout manager based on the tiles that you add to the panel. The size should not change as tiles are removed.
I ultimately decided to place the background image in the card panel itself, then put the card panel in a box layout manager so that it's always centered. I renamed cardPanel to gameBoard. Definitely could be cleaner, but I can only work with my requirements.
setSize(new Dimension(1000, 600));
gameBoard = new JPanel();
gameBoard.setLayout(null);
gameBoard.setOpaque(false);
Dimension expectedDimension = new Dimension(920, 500);
gameBoard.setPreferredSize(expectedDimension);
gameBoard.setMaximumSize(expectedDimension);
gameBoard.setMinimumSize(expectedDimension);
//add cards to gameBoard here
JLabel background = new JLabel( new ImageIcon( getClass().getResource("images/graphic.png") ) );
background.setLocation(79,0); //manually center graphic
background.setBounds(new Rectangle(0, 0, 920, 500));
gameBoard.add(background);
Box centerBox = new Box(BoxLayout.Y_AXIS);
centerBox.setOpaque(true);
centerBox.setBackground(Color.WHATEVER);
centerBox.add(Box.createVerticalGlue());
centerBox.add(gameBoard);
centerBox.add(Box.createVerticalGlue());
add(centerBox);
setVisible(true);
I have a JFrame, in this JFrame I have a JPanel that I draw on, this Panel can be any size and so I placed it into a JScrollpane to let me scroll when the panel is larger than the window screen size.
Unfortunately does not work as I expected:
Making the JFrame window smaller than the JPanel size does not show scroll bars
The JScrollPane size now seems locked to the size of the JPanel I have added to it, where as before it resized to the bounds of it's JFrame window (it still kinda does this but only vertically now?!)
The JPanel seems to assume the size of the JScrollpane regardless of what I set for preferred size
I am sure I'm doing something stupid, if someone could point out what I would be most grateful!
JPanel imageCanvas = new JPanel(); // 'Canvas' to draw on
JScrollPane scrollPane = new JScrollPane();
// set size of 'canvas'
imageCanvas.setMinimumSize(new Dimension(100,100));
// Scroll pane smaller then the size of the canvas so we should get scroll bars right?
scrollPane.setMinimumSize(new Dimension(50,50));
// Add a border to 'canvas'
imageCanvas.setBorder(BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
scrollPane.setViewportView(imageCanvas);
setPreferredSize() is the trick, setMinimumSize() and even setSize() on the component will be ignored by JScrollPane. Here's a working example using a red border.
import java.awt.*;
import javax.swing.*;
public class Scroller extends JFrame {
public Scroller() throws HeadlessException {
final JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createLineBorder(Color.red));
panel.setPreferredSize(new Dimension(800, 600));
final JScrollPane scroll = new JScrollPane(panel);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
add(scroll, BorderLayout.CENTER);
setSize(300, 300);
setVisible(true);
}
public static void main(final String[] args) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Scroller().setVisible(true);
}
});
}
}
// suggest a size of 'canvas'
_ImageCanvas.setPreferredSize(new Dimension(100,100));
// Scroll pane smaller then the size of the canvas so we should get scroll bars right?
_ScrollPane.setPreferredSize(new Dimension(50,50));
// ..later
_Frame.pack();
Set preferred size on the canvas.
Increase dimensions 100,100 is too small atleast on my computer.
You may want to use new GridLayout(1,1); for you JFrame if you want the scrollpane to expand when you expand the frame.
As far as I remember there are 2 options:
define the preferredSize of _ImageCanvas
create a subclass of JPanel and implement Scrollable: http://docs.oracle.com/javase/7/docs/api/javax/swing/Scrollable.html
For more details, see the Javadoc:
http://docs.oracle.com/javase/7/docs/api/javax/swing/JScrollPane.html
Check out the Scrollable interface, this may help with the sizing issues.
These two method maybe helpful:
boolean getScrollableTracksViewportWidth();
boolean getScrollableTracksViewportHeight();