Size of JFrame odd after pack() - java

I have got this piece of code:
frame.setTitle("SideScroller");
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
Dimension dim = new Dimension(1000, 500);
frame.setContentPane(gamePanel); //gamePanel is an extended JPanel
gamePanel.setPreferredSize(dim);
frame.pack();
After I run this code and fill the gamePanel with parts of 50x50p, which should fill the gamePanel and thus the frame completely, there are some empty rows of pixels at the right and bottom of the screen.
frame.getSize(); //gives us: 1016 x 539
frame.getInsets(); //gives: Top: 31 | Left: 8 | Bottom: 8 | Right: 8
An image of the result:
As you can see, there is still some empty room in the JFrame.
The code to fill the gamePanel:
//Fill the panel with landscape
//One part : 50x50 px
//Panel : 1000x500 px
//Width : 20 prts
//Height : 10 prts
#Override
public void paint(Graphics g){
super.paint(g);
for(int i = 0; i < 20; i++){
for(int j = 0; j < 10; j++){
//Decide which landscape-img should be used
switch(levelOne[j][i]){
case 0:g.drawImage(new ImageIcon("resources\\landscape-img\\air.png")
.getImage(), 50 * i, 50 * j, null);
break;
case 1:g.drawImage(new ImageIcon("resources\\landscape-img\\ground.png")
.getImage(), 50 * i, 50 * j, null);
break;
}
}
}
g.dispose();
}
I do not see why the frame isn't filled fully. Hope someone can help me!

Few hints to help you to solve your problem.
1) If you need your game panel have a preferred size, then just override getPreferredSize() like this:
public class GamePanel extends JPanel {
...
#Override
public Dimension getPreferredSize() {
return isPreferredSizeSet()
? super.getPreferredSize() : new Dimension(1000, 500);
}
...
}
See also Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
2) Be aware that you should not override paint() method but paintComponent() instead. See A Closer Look at the Paint Mechanism.
public class GamePanel extends JPanel {
...
#Override
protected void paintComponent(Graphics g) {
// Your implementation here
}
...
}
3) As already suggested just add your gamePanel to the frame's content pane directly:
frame.getContentPane().add(gamePanel);
This way you will take advantage of its default Layout Manager: BorderLayout to make your panel fill the whole center area.
4) Finally, while you can perfectly use Swing to make games it's not the best API to do so because it is designed for desktop applications. You might want to consider use some library intended to games development such as libgdx

Found the answer here. I called frame.setResizable(false) after I called frame.pack(), apparently that messes the frame up. You have to call that before pack() (or `setVisible(true)).

Related

JAVA, display phrase in increasing font sizes in a frame

I am having an issue getting this code to run properly. It compiles and initially the frame displays properly. The problem is that when I manually re-size the frame by either maximizing or by dragging the side of the frame over, the text disappears. I am using jGRASP, not sure if that is the issue or not. The code seems to make sense to me, and like I said, it compiles(I know that does not necessarily make it right). I'm still a newbie at this so if anyone can point me in the right direction I would be very appreciative.
import javax.swing.*;
import java.awt.*;
public class JFontSizes extends JFrame {
int x = 5;
int y = 50;
String homework = "This is the first homework assignment";
public JFontSizes() {
super("Increasing Font Sizes");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint(Graphics brush) {
super.paint(brush);
// This works sometimes. I am not sure if it is a jGRASP issue or something else.
// If I resize the frame, the text disappears, and I cannot get the text to start at the top of the frame
for(int n = 6; n<= 20; ++n) {
brush.setFont(new Font("Serif", Font.PLAIN, n));
brush.drawString(homework, x, y);
y += 15;
}
}
public static void main(String[] args) {
JFontSizes frame = new JFontSizes();
frame.setSize(400, 500);
frame.setVisible(true);
}
}
When first time paint() is called the value of y was 5. And it is incremented in a loop. So that before leaving paint() its value will be 275.
But when you resize your frame paint() is called again and this time the value of y is 275 and when brush.drawString(homework, x, y); is called the homework is printed at 275px bottom from top left corner.
So what you need to do is re-initialize y every time :
public void paint(Graphics brush) {
y = 50;
....
Edit :
As commented by camickr you should override paintComponent(...) instead of paint(...) until you have some specific reason to override paint().
And you mean you are not able to print text at top (even in beginning) then it is because you had initialized y with 50. Which means the text will be drawn at 50px from top.

in which order are components added to a JPanel drawn?

I have a small application that should demonstrate how the opaque property works in Swing.
however, what throws me off is the order in which paintComponent() is called. I'd come to think components are drawn in the order they were added (whats added first, gets drawn first) however in this example it appears that paintComponent() methods are drawn in reverse order(what's added last got painted first)
can someone explain this behavior, thanks
public class TwoPanels {
public static void main(String[] args) {
JPanel p = new JPanel();
// setting layout to null so we can make panels overlap
p.setLayout(new BorderLayout());
CirclePanel topPanel = new CirclePanel("topPanel1");
// drawing should be in blue
topPanel.setForeground(Color.blue);
// background should be black, except it's not opaque, so
// background will not be drawn
topPanel.setBackground(Color.black);
// set opaque to false - background not drawn
topPanel.setOpaque(false);
topPanel.setBounds(50, 50, 100, 100);
// add topPanel - components paint in order added,
// so add topPanel first
p.add(topPanel);
CirclePanel bottomPanel = new CirclePanel("buttomPanel1");
// drawing in green
bottomPanel.setForeground(Color.green);
// background in cyan
bottomPanel.setBackground(Color.cyan);
// and it will show this time, because opaque is true
bottomPanel.setOpaque(true);
bottomPanel.setBounds(30, 30, 100, 100);
// add bottomPanel last...
p.add(bottomPanel);
// frame handling code...
JFrame f = new JFrame("Two Panels");
f.setContentPane(p);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
// Panel with a circle drawn on it.
private static class CirclePanel extends JPanel {
String objName;
public CirclePanel(String objName) {
this.objName = objName;
}
// This is Swing, so override paint*Component* - not paint
protected void paintComponent(Graphics g) {
System.out.println(objName);
// call super.paintComponent to get default Swing
// painting behavior (opaque honored, etc.)
super.paintComponent(g);
int x = 10;
int y = 10;
int width = getWidth() - 20;
int height = getHeight() - 20;
g.fillArc(x, y, width, height, 0, 360);
}
}
}
From Swing Internals: Paint Order:
What's going wrong under the hood?
A container holds an array with all child components. For painting, Swing (more precise JComponent#paintChildren()) iterates over the array in reverse order - this means the first added component will be painted at last. Z-order modifies the child position within this array. In case that the layout manager makes use of Container#getComponents() (as many Swing core layout managers do) there's no guarantee that the array order represents the order in which the components have been added to the container.
Generally in Swing you can specify the paint order by applying component Z-Order (see Container#setComponentZOrder). This method is useful as long as you're using a null layout or a layout manager which makes use of constraints.
The disadvantage of using #setComponentZOrder is that it can affect component position.

graphics not showing on resized subpanel?

I am having issues with a small project im working on. I am trying to create a Moveable message panel when holding down the mouse button but i am stuck on one part.
I want to place the A small panel with a size of 50x30 pixels that contains the message "java" in it and have this small panel in a larger panel and place that panel into my JFrame.
However, when i do so the message "java" disappears and only the the small panel in the larger panel appears. I added borders to my panels to make sure that my panels were actually visible. Please help and here is my code:
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;
public class MovingPanel extends JFrame {
private String message;
private int x = 100;
private int y = 100;
public MovingPanel() {
JPanel panel = new JPanel();
MessagePanel p1 = new MessagePanel("Java");
panel.setBorder(new LineBorder(Color.RED, 2));
panel.setLayout(null);
p1.setLocation(x, y);
p1.setSize(50, 30);
p1.setBorder(new LineBorder(Color.BLACK, 2));
p1.setLayout(new BorderLayout());
panel.add(p1);
add(panel);
}
public static void main(String[] args) {
MovingPanel frame = new MovingPanel();
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Test Message Panel");
frame.setVisible(true);
}
class MessagePanel extends JPanel {
public MessagePanel(String s) {
message = s;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString(message, x + 20, y + 10);
}
}
}
Maybe you can try to use a simple JLabel component instead of your "MessagePanel".
First thing you need to understand is this.
The second and third arguments of this g.drawString(message, x + 20, y + 10); method are the x and y location of the panel.
With the above being said, you have to remember that it is the x and y location of the containing panel, which is MessagePanel.
You have the size of your MessagePanel object set at 50, 30, yet you are trying to access a point 120 (x + 20) and 110 (100 + 10), which does not exist since you size the size of the panel.
So now that's understood, let's say you want to paint the message at the very left corner of the MessagePanel, so you try and do this g.drawString(message, 0, 0);. This still would show anything as the point starts from the bottom left corner of the message, so the message would actually be riding just above the visible area.
When drawing strings, you need to consider the FontMetrics, which allows you to get the size of the string you are trying to draw, so you can position the message exactly on the screen where you want it.
A simple fix would be just set an x and y a little above 0, 0, like 15, 15. Though this might get your message to draw, it wouldn't be centered. You can keep on changing and getting different numbers to check if it is aligned in the middle, but the proper way is to use FontMetrics
As a said a simple (but maybe not desired) fix is to just change this
g.drawString(message, x + 20, y + 10);
To
g.drawString(message, 15, 15);
And you will see the message.
Instead of what you are doing though, this is how I would do it.
Instead of using two panels, I would just use one - the one that's doing the painting.
Don't set the size of it, instead override getPrefferedSize inside that class, to whatever size you want the main panel to me.
When you draw, just draw a rectangle the size you want at the specified coordinates.
Also draw the message in the same paintComponent method.
call pack() on the JFrame.
If you do the above, there's no need to try and move the location of the MessagePanel. Instead move the x and y coordinates when you call repaint, You can have offsets for the message. Like
int boxX = 100;
int boxY = 100;
int messageOffset = 15;
Then you can paint like this
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawRect(boxX, boxY, 50, 30);
g.drawString(message, boxX + messageOffset, boxY + messageOffset);
}
Now in your action methods, just alter the boxX and/or boxY and call repaint.
Also, if you want a thicker line, look into Graphics2D API, you can setStroke.

Force size restrictions on Applet embedded in html

I have a Java Applet with a GridLayout containing widgets which I wish to be square, and remain tightly packed to each other (so their sizes are unrestricted).
However, I wish for the GridLayout to take up as much space as possible before being too large for the screen or unable to preserve widget 'squareness'.
Note that the number of rows and columns in the GridLayout are not necessarily equal (the Grid as a whole can be non-square)
This Applet is displayed via this html file;
<html>
<body>
<applet code=client.Grid.class
archive="program.jar"
width=100% height=95%>
</applet>
</body>
</html>
Currently, this makes the Applet expand into the window it is put in; the Grid can be resized by resizing the window, but this causes the geometry of each widget to be changed (losing 'squaredness').
So; where and how do I place these geometrical restrictions?
It can't be in the html file alone, since it has no knowledge of row/column count, and so doesn't know the best size to make the Applet.
However, I don't know how to set the size on the GridLayout or the Panel containing it, since it must know the viewing-browser's page size (to make it as large as possible) and I'm of the impression that the html specified geometry overrides the Applet specified.
EDIT:
Attempting to implement Andrew's suggestion;
screen = new JPanel(new GridLayout(rows, columns)) {
public Dimension getPreferredSize() {
Dimension expected = super.getPreferredSize();
// calculate preferred size using expected, rows, columns
return new Dimension(100, 100) // testing
}
public Dimension getSize() {
return getPreferredSize();
}
};
I understand this ignores the 'minimum size' stuff, but that doesn't matter at the moment.
Screen is placed in the center of a border layout, containing other widgets
getContentPane().add(screen, BorderLayout.CENTER);
getContentPane().add(otherWidgets, BorderLayout.PAGE_END);
I know this doesn't make screen centered in the space it has, but that's not entirely necessary at the moment so I want to keep things as simple as possible.
This isn't at all working; there's no visible difference from what I had before (when viewed through Eclipse; I haven't even reached the html stage yet) excepting the minimum size stuff. The screen component is still being re-sized by the applet at leisure, making the cells 'unsquare'. What am I doing wrong?
Put the grid layout container into a grid bag layout as the only component with no constraint, as seen in this answer. That will center it.
Update
And of course, put it in a component that returns a preferred size equating to the maximum square size it can manage depending on the parent size. Such as in SquarePanel.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
/**
* A square panel for rendering. NOTE: To work correctly, this must be the only
* component in a parent with a layout that allows the child to decide the size.
*/
class SquarePanel extends JPanel {
#Override
public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
System.out.println("Preferred Size: " + d);
int w = (int) d.getWidth();
int h = (int) d.getHeight();
// Set s to the larger of the mimimum component width or height
int s = (w > h ? w : h);
Container c = getParent();
if (c != null ){
Dimension sz = c.getSize();
if ( d.getWidth()<sz.getWidth() ) {
// Increase w to the size available in the parent container
w = (int)sz.getWidth();
System.out.println("WxH: " + w + "x" + h);
// recalculate s
s = (w < h ? w : h);
}
if ( d.getHeight()<sz.getHeight()) {
// Increase h to the size available in the parent container
h = (int)sz.getHeight();
System.out.println("WxH: " + w + "x" + h);
// recalculate s
s = (w < h ? w : h);
}
}
// Use s as the basis of a square of side length s.
System.out.println("Square Preferred Size: " + new Dimension(s, s));
return new Dimension(s, s);
}
#Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
#Override
public Dimension getSize() {
return getPreferredSize();
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
// the GUI as seen by the user (without frame)
// A single component added to a GBL with no constraint
// will be centered.
JPanel gui = new JPanel(new GridBagLayout());
gui.setBackground(Color.BLUE);
SquarePanel p = new SquarePanel();
p.setBorder(new EmptyBorder(5,15,5,15));
p.setLayout(new GridLayout(3,0,2,2));
for (int ii=1; ii<13; ii++) {
p.add(new JButton("" + ii));
}
p.setBackground(Color.red);
gui.add(p);
JFrame f = new JFrame("Demo");
f.add(gui);
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See https://stackoverflow.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
// should be done last, to avoid flickering, moving,
// resizing artifacts.
f.setVisible(true);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}

Java GridLayout is laying out all my JPanels in the top left corner

SOLVED:
Just found out what the problem was, after trying to make an SSCCE.
It had to do with my cell class, I didn't realise I was overriding getX() and getY() from the JComponent class.
After renaming these accessors it all works as expected
========================================
I have a JPanel with a GridLayout set at 3 rows x 3 cols.
I'm trying to add JPanels to each cell in the gridlayout to fill up all 9 cells.
Each one of these JPanels has an overriden paintChildren method which will paint some kind of rectangle starting at the top left of the JPanel - the end result will be each cell has a rectangle in it starting at the top left of the cell.
After adding all the JPanels to the gridlayout, they all appear in the top left corner overlapping each other (I have confirmed they are overlapping), instead of being laid out in a 3x3 grid.
How can I get them arranged in the 3x3 grid?
(Simplified) Code:
public class Panel extends JPanel {
public Panel(int x, int y) {
layout = new GridLayout(x, y, 2, 2);
setLayout(layout);
populateGrid();
}
public void populateGrid() {
removeAll();
for (int i = 0; i < 9; i++)
add(new Cell(50,50));
}
}
public class Cell extends JPanel {
public Cell(int x, int y) {
// x/y values used to define rectangle
setBorder(BorderFactory.createLineBorder(new Color(0,0,0)));
setBackground(Color.WHITE);
}
public void paintChildren(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(0, 0, x, y);
}
}
Make sure you import the right Panel. (And not the java.awt.Panel class.) Or, better, rename your class to GridPanel or something similar to avoid confusion / clashes.
You probably don't want to override paintChildren. Overriding paintComponent sounds like a better option in this case.
You may want to set minimum / preferred / maximum size for the Cell class.
Hard to make more observations without the actual code that instantiates and uses your Panel class.

Categories