For my assignment, I am given this piece of code:
// This class/method uses a global variable that MUST be set before calling/using
// note: You can not call the paint routine directly, it is called when frame/window is shown
// look up the repaint() routine in the book
// Review Listings 8.5 and 8.6
//
public static class MyPanel extends JPanel {
public void paintComponent (Graphics g) {
int xpos,ypos;
super.paintComponent(g);
// set the xpos and ypos before you display the image
xpos = 10; // you pick the position
ypos = 10; // you pick the position
if (theimage != null) {
g.drawImage(theimage,xpos,ypos,this);
// note: theimage global variable must be set BEFORE paint is called
}
}
}
My professor also says: You will also need to to look up how to create AND add a JPanel to a JFrame. If you can create AND add a JPanel, then all you need to do is substitute 'MyPanel' for the class name 'JPanel' and this code will display an image on the window frame.
What does he mean by "then all you need to do is substitute 'MyPanel' for the class name 'JPanel' and this code will display an image on the window frame"? I'm confused as to where I'm supposed to substitute MyPanel. Here's my code:
public static class MyPanel extends JPanel {
public void paintComponent (Graphics g) {
int xpos,ypos;
super.paintComponent(g);
JPanel panel= new JPanel();
JFrame frame= new JFrame();
frame.setSize(500,400);
frame.add(panel);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// set the xpos and ypos before you display the image
xpos = 600; // you pick the position
ypos = 600; // you pick the position
if (theimage != null) {
g.drawImage(theimage,xpos,ypos,this);
// note: theimage global variable must be set BEFORE paint is called
}
}
}
If I understand what you're asking right... In your assignment, you're being asked to extend JPanel for your own needs. Note how you would add a JPanel if it were not being extended:
JFrame myFrame = new JFrame();
JPanel myPanel = new JPanel();
myFrame.add(myPanel);
myFrame.pack();
myFrame.setVisible(true);
This adds the JPanel to the JFrame, packs it and sets it to be visible. Since your myFrame class extends JPanel, you should be able to do something very similar by creating a new instance of your panel class and adding it to your JFrame.
You won't want to be doing this part in paintComponent(), as paintComponent() can potentially be called multiple times. Check here to see what paintComponent() does.
#Hyper Anthony
So it would be something similar to this?:
MyPanel Mypanel= new MyPanel();
JFrame Myframe= new JFrame();
Myframe.setSize(500,400);
Myframe.add(Mypanel);
Myframe.setVisible(true);
Myframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Related
I want to use a JLabel in a very simple environment but I'm wondering why I have to set the location on every repaint.
Code:
public class Example {
public static void main(String[] args) {
JFrame frame = buildFrame();
TestPane pane = new TestPane();
frame.add(pane);
while (true) {
pane.repaint();
frame.setVisible(true);
}
}
private static JFrame buildFrame() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(480, 272);
frame.setVisible(true);
return frame;
}
}
public class TestPane extends JPanel {
JLabel testLabel = new JLabel("TEST");
TestPane() {
super();
add(testLabel);
testLabel.setLocation(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
testLabel.setLocation(200, 200); // without this line, the label will always be at the top center
}
}
The loop-based layout comes from various animations with images I'm doing. Why does the repaint always reset the location of all labels so I have to setLocation on every paintComponent?
why I have to set the location on every repaint.
You don't. Actually, you should never set the position or any kind of constraint of a component inside paintComponent method. paintComponent method is only for painting, not for orientation or anything else.
When you jpanel.add(myComponent, constraints) component's position will be decided by container's current LayoutManager. (When you jpanel.add(myComponent); without any constraints, the default constraints will take place, with every layout manager having its own default).
The label is placed at the top of the panel because you do not set the layout of the panel, so it has its default, which is FlowLayout. In order to change it you will have to use another layout manager with the proper constraints.
For example, in order to place it at the center of the panel, you must do:
jpanel.setLayout(new BorderLayout());
jpanel.add(myLabel,BorderLayout.CENTER);
Finally doing while(true) inside the Thread where you GUI is running, it will hang the thread, means that the GUI will be "frozen" since events cannot take place.
So I have two classes, one is in charge of creating the JPanel and also overrides paint. The other contains the main and uses this other class. For the sake of ease, I've cut out what isn't involved in graphics. This first one does the JPanel and paint. The second is the main. I apologize for any poor structure or mess, I'm relatively new and I've just been throwing things in to see what works. The stuff in the paint is only a test, it isn't the primary goal.
public class PokerTable extends JPanel {
private static final int WIDTH = 800;
private static final int HEIGHT = WIDTH * 3 / 4;
private static final String NAME = "Test";
private JFrame frame = new JFrame(NAME);
public PokerTable() {
//frame.setMinimumSize(new Dimension(WIDTH,HEIGHT));
//frame.setMaximumSize(new Dimension(WIDTH,HEIGHT));
frame.setPreferredSize(new Dimension(WIDTH,HEIGHT));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setBackground(Color.GREEN);
g2d.setColor(Color.RED);
g2d.fillOval(0, 0, 30, 30);
}
}
This next one does more than call the PokerTable, but I've left the other parts out as they rely on other classes and just print to console.
public class Poker{
public static void main(String[]args) {
System.out.println("hello");
PokerTable pt = new PokerTable();
pt.repaint();
}
Not that I necessarily expected it to work, I've changed the extension on PokerTable to Canvas and JFrame without any luck.
You're not adding the panel to the frame, try with:
frame.add(this);
in PokerTable constructor
You need to add the panel to the frame, or the panel will never repaint.
frame.add(panel) or frame.add(this)
is what you need to call depending on where you do it.
Also, you should be overriding paintComponent, and never paint directly. that won't cause your code to fail, but it is bad practice.
Newbie, so bear with me. I am writing a Sokoban game in Java, I got the game up and going but I am having problems with pack(). I want my frame to resize itself depending on the size of the level, different levels have different sizes. The panel is being painted correctly for the different sizes, so if I just maximize the frame then everything is good, but how do I invoke pack() to automatically resize the frame? I tried to put pack() in my Main method, but I suspect the solution is not so simple (probably the way I have structured my program does not help either). Putting pack() in the Main method produces the image attached, a very small rectangle with basically just the min,max and close buttons.
The recommended solution that I would like to implement is as follows:
write your Sokoban constructor so that it takes the surrounding JFrame as a reference parameter that your object then remembers in a field. Then after you change the preferred size of your Sokoban component, call the method pack for this stored surrounding JFrame.
I have attached the code for my constructor and Main method.
public class Sokoban extends JPanel {
LevelReader lReader = new LevelReader();
private static final int SQUARESIZE = 50; // square size in pixels
int currentLevel = 0;
int height = 0;
int width = 0;
int x = 0;
int y = 0;
Contents [][] mapArray;
public Sokoban(String fileName) {
lReader.readLevels(fileName);
initLevel(currentLevel);
KeyListener listener = new MyKeyListener();
addKeyListener(listener);
this.setPreferredSize(new Dimension(500,500));
setFocusable(true);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Sokoban");
Sokoban sokoban = new Sokoban("m1.txt");
frame.add(sokoban);
frame.setVisible(true);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
write your Sokoban constructor so that it takes the surrounding JFrame as a reference parameter that your object then remembers in a field. Then after you change the preferred size of your Sokoban component, call the method pack for this stored surrounding JFrame
Answer was updated to use key binding as suggested by #mKorbel (thanks for tip, code looks cleaner now)
What your fried was trying to tell is to create something like this (I removed your code that wasn't necessary in this example)
class Sokoban extends JPanel {
private JFrame frame;
private class MyAction extends AbstractAction {
private Dimension dimension;
public MyAction(Dimension dimension) {
this.dimension = dimension;
}
#Override
public void actionPerformed(ActionEvent e) {
//we will pack only when dimensions will need to change
if (!getPreferredSize().equals(dimension)) {
setPreferredSize(dimension);
frame.pack();
}
}
}
public Sokoban(String fileName, JFrame tframe) {
this.frame = tframe;
setFocusable(true);
setPreferredSize(new Dimension(100, 100));
setBackground(Color.red);
add(new JLabel("press A, S, D"));
getInputMap().put(KeyStroke.getKeyStroke('a'), "typed a");
getInputMap().put(KeyStroke.getKeyStroke('s'), "typed s");
getInputMap().put(KeyStroke.getKeyStroke('d'), "typed d");
getActionMap().put("typed a", new MyAction(new Dimension(100, 100)));
getActionMap().put("typed s", new MyAction(new Dimension(200, 100)));
getActionMap().put("typed d", new MyAction(new Dimension(100, 200)));
}
public static void main(String[] args) {
JFrame frame = new JFrame("Sokoban");
Sokoban sokoban = new Sokoban("m1.txt", frame);
frame.setContentPane(sokoban);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
In constructor public Sokoban(String fileName, JFrame tframe) you need to pass reference to frame that will contain your Sokoban panel. You will need to store object from that reference somewhere in your class, like in class field private JFrame frame;.
Now thanks to that reference whenever you change size of your panel you can use it to change size of frame containing that panel by invoking frame.pack() and make it to adapt to new size.
I need to pass a JPanel extending class to the main class.
Here is what I have so far:
Main class
import java.awt.Color;
import javax.swing.*;
public class main {
private gamePanel gamepanel = new gamePanel();
public JPanel createContentPane(){
// We create a bottom JPanel to place everything on.
JPanel totalGUI = new JPanel();
//We set the Layout Manager to null so we can manually place
// the Panels.
totalGUI.setLayout(null);
//Now we create a new panel and add it to the bottom JPanel.
totalGUI.add(gamepanel);
totalGUI.setOpaque(true);
return totalGUI;
}
private static void createAndShowGUI(){
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("[=] There's a JPanel in here! [=]");
//Create and set up the content pane.
main demo = new main();
frame.setContentPane(demo.createContentPane());
//The other bits and pieces that make your program a bit more stable.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(700,500);
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a jog for the event-dispatching thread:
//creating and showing this application's GUI.
System.out.println(gamepanel);
SwingUtilities.invokeLater(new Runnable() {
public void run(){
createAndShowGUI();
}
});
}
}
The gamePanel class
public class gamePanel extends JPanel implements Commons {
private Dimension d;
private ArrayList snowmens;
private coreFunctions functions = new coreFunctions();
private int snowmenX = 150;
private int snowmenY = 5;
private final String snowmenpix = "snowman.png";
private JPanel background;
public gamePanel() {
add(new JLabel("TEST"));
setFocusable(true);
setDoubleBuffered(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(0, 0, 700, 700);
}
}
I can't figure out why the blue background and the label is not being shown...
EDIT:
Here are more details:
Ok so I am trying to make a little 2D game. For that I need to create some snowmen on the gamePanel class and display it via the main class. To start it off, the createContentPane creates a background panel, the root panel if you want. The createandshowgui creates a JFrame.
The gamepanel class is in fact a JPanel which has 1 panel as of now, which is the background panel. For now, I only want it to have a blue background.
I tried putting it like this because I saw some examples and they were pretty similar to what I have, but for some reason, I can't get it to work....
Thank you,
Ara
You should use LayoutManager instead of setLayout(null); and if only one component is being added no need for it either (that component will stretch to fill unless others are added)
see here for tutorial on LayoutManagers:
http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
http://docs.oracle.com/javase/tutorial/uiswing/layout/using.html
If all you are trying to do is have a blue background (on which you can add components) then simply override paintComponent() of your gamePanel and paint the background blue:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
}
then you can add your JLabel as if it was a normal Panel as the background is now being painted blue and not set by/as a component.
If you have an image look into g.drawImage(Image image,int x,int y,ImageObserver iO)
EDIT
DROP THE CALL TO:
setLayout(null);
and it will work
I'm having problems adding a JPanel, which has a paintComponent in it, to a JFrame.
If this is the only thing I add to the frame, it works. But as soon as I add a layout manager and add other components to the JFrame, it no longer shows the panel with the painting!
To make this clearer ...
This is the code that works and the JPanel is successfully shown:
The panel that draws the sign (in reality i am not trying to paint hello, this is to simply the code here)
public class SignPanel2 extends JPanel {
public int hello;
public void paintComponent(Graphics comp) {
Graphics g = (Graphics) comp;
g.setColor(Color.LIGHT_GRAY);
g.fillRect(70, 250, 150, 150);
g.setColor(Color.BLACK);
if (hello > 0)
g.drawString("h",135, 270);
if (hello > 1 )
g.drawString("h e",135, 270);
if (hello > 2)
g.drawString("h e l",135, 270);
if (hello > 3)
g.drawString("h e l l",135, 270);
if (hello > 4)
g.drawString("h e l l o",135, 270);
}
}
The frame i put the panel on:
public class SignFrame extends JFrame {
// the constructor method
public SignFrame () {
super("This is the title of the Sign Frame");
setSize(300,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// make a container for the frame
Container content = getContentPane();
// call from the drawing panel
SignPanel2 signpanel = new SignPanel2();
// change class variable of SignPanel
signpanel.hello = 5;
signpanel.repaint();
// add signpanel to container
content.add(signpanel);
setContentPane(content);
setVisible(true);
}
}
The main class
public class TheSignMain {
public static void main (String[] args) {
SignFrame signframe = new SignFrame();
}
}
The above works perfectly fine and gives me a frame with the desired drawing in it.
But if I add other components to the frame and add a layout manager, it no longer shows me the painting. even if I use repaint().
I have to include a layout manager, otherwise it adds the panel with the painting, but not the other components.
This is how my frame class looks now, and this is where i'm having problems.
public class SignFrame extends JFrame {
// the constructor method
public SignFrame () {
super("This is the title of the Sign Frame");
setSize(300,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// make a container for the frame
Container content = getContentPane();
// need a layout manager to decide the arrangement of the components on the container
FlowLayout flo = new FlowLayout();
// designate the layout manager to the container
content.setLayout(flo);
// make components
JPanel buttons = new JPanel();
JButton play = new JButton("Play");
JButton pause = new JButton("Pause");
JButton stop = new JButton("Stop");
// add components to a panel
buttons.add(play);
buttons.add(pause);
buttons.add(stop);
// add panel to frame container
content.add(buttons);
// call from the drawing panel
SignPanel2 signpanel = new SignPanel2();
// change class variable of SignPanel
signpanel.hello = 5;
signpanel.repaint();
// add signpanel to container
content.add(signpanel);
setContentPane(content);
setVisible(true);
}
}
I am totally new to Java, so any help will be much appreciated.
Sorry for all that code and thanks for your help!!
Not tested, but the flow layout probably uses the preferred size of your panel, and you probably haven't overridden getPreferredSize() to return something other than a [0, 0] dimension.
Also, you should encapsulate the change of the hello variable in a setHello() method that calls repaint(). The calling code shouldn't have to deal with repaint. The panl should know when it has to be repainted, and call repaint itself.