cant understand why jpanel doesnt render - java

im doing a final project for school, but my code is doing something really weird..
if i call a function within the class, the jpanel is displayed, but when i call this function from another class, it doesnt..
I found a solution..
calling a method repaint (); after creating 2jpanels.
solve it.
now... how can I close this question, or mark it as solved??
thx alot to everyone who read this.
here is the class.
main class
public class Main {
public static void main(String[] args) {
new GameFrame();}
}
GameFrame class
public class GameFrame extends JFrame {
private static final long serialVersionUID = 1L;
private static final int baseWidth=192,baseHeight=352;
private JPanel panel;
private GamePanel gamePanel;
public GameFrame() {
panel = (JPanel) this.getContentPane();
Dimension d = new Dimension(baseWidth, baseHeight);
panel.setPreferredSize(new Dimension(d));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setTitle("test game..");
this.setResizable(false);
this.setLocationRelativeTo(null);
this.setVisible(true);
init_panelGame();//this will content the real game panel.
}
private void init_panelGame() {
gamePanel = new GamePanel(this,scale);
gamePanel.setVisible(true);
panel.add(gamePanel);
this.revalidate();}
}
GamePanel class
public class GamePanel extends JPanel {
private static final long serialVersionUID = 1L;
private int imgSize = 32;
public String gameState ="create_row";
private ImagePanel[][] mat_pos = new ImagePanel[6][11];
private Timer tiempo;//changed to a swing timer..
public GamePanel(GameFrame gp,int scale) {
this.setLayout(null);
createRow();//works fine if i call this method from here.
tiempo=new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
gameFlow();
}});
timerStart();
}
public void gameFlow() {//this is the method call by my timer
if (gameState.equals("create_row"))
{createRow(); //it doesnt render the created row!! this is my problem!!
gameState = "move_squares";
return;}
if (gameState.equals("move_squares"))
{moveSquares();return;}
}
private void moveSquares() {
int runningSquares = 0;
for (int col=0;col<6;col++)
for(int ren=10;ren>=0;ren--)
{if (mat_pos[col][ren]!=null)
if (mat_pos[col][ren].state.equals("running"))
{runningSquares++;
mat_pos[col][ren+1]=mat_pos[col][ren];
mat_pos[col][ren]=null;
if (ren+1==10 || mat_pos[col][ren+2]!=null)
mat_pos[col][ren+1].state = "stopped";
int newX,newY;
newX = imgSize * col;
newY = imgSize * (ren+1);
mat_pos[col][ren+1].setBounds(newX,newY, imgSize,imgSize);
}
}
if (runningSquares==0)
{gameState="create_row";
createRow();}
}
public void createRow() {//create 2 panels and add them to this jpanel.
gameState = "move_squares";
int colorRandom1 = (int) (Math.random()*5);
int colorRandom2 = (int) (Math.random()*5);
ImagePanel t1 = new ImagePanel(colorRandom1);
ImagePanel t2 = new ImagePanel(colorRandom2);
int columna = (int) (Math.random()*5);
mat_pos[columna][0] = t1;
mat_pos[columna+1][0] = t2;
t1.setBounds(columna*imgSize,0,32, 32);
t2.setBounds((columna+1)*imgSize,0,32, 32);
this.add(t1);
this.add(t2);
this.revalidate();
}
public void timerStart(){
tiempo.start();
}
public void timerStop(){
tiempo.stop();
}
}
//finallly the image panel class wich only creates a simple panel with an img as background
public class ImagePanel extends JPanel {
private static final long serialVersionUID = 1L;
public String state = "running";
private Image img;
private size = 1;
public ImagePanel(int color) {
String cad=null;
this.setVisible(true);
cad = "assets/blue.png";
this.img = new ImageIcon(cad).getImage();
crearFondo();
}
public void crearFondo() {
Dimension size = new Dimension(32,32);
setPreferredSize(size);
setMinimumSize(size);
setMaximumSize(size);
setSize(size);
setLayout(null);
}
public void paintComponent(Graphics g) {
g.drawImage(img, 0,0, null);
}
}

First of all, you generally need to revalidate after adding things to a container if it is already attached to the component hierarchy and visible.
Try calling this.revalidate() after the this.add() call in your createRow method.
Secondly, you seem to be using a java.util.Timer. This will not call you on the EDT. Use a javax.swing.Timer instead or make sure that createRow switches back to the EDT. This can be done with SwingUtilities.invokeLater(new Runnable() {...}). You should fix this no matter if my first suggestion works!
If that does not work, please provide a SSCCE, ideally one that I can copy-paste and try on my end.

Related

Java simple JFrame is null

I have made simple JFrame that works fine except when I am trying to access it from other classes it returns null. So I made getter for it (public static JFrame getWindow()) it returns null. If I set JFrame to public and try to access it that way it returns null. When I create it becomes null right after the game engine starts.
Main:
public class Main {
private static String title = "2D SquareWorld 0.";
private static String version = "";
private static JFrame window;
private static Container container;
public static void main(String[] args) {
GameEngine game = new GameEngine();
window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setTitle(title + Version.newVersion());
window.setResizable(false);
window.add(game);
window.setSize(1000, 720);
window.setLocationRelativeTo(null);
window.setVisible(true);
game.start();
}
public static Container getContainer() {
return container;
}
public static JFrame getWindow() {
return window;
}
}
GameEngine:
package GameEngine;
public class GameEngine extends Canvas implements Runnable, KeyListener, MouseListener, MouseMotionListener {
private static final long serialVersionUID = 1L;
private Thread thread;
private boolean running;
private GameStateManager gsm;
public GameEngine() {
gsm = new GameStateManager();
}
public void start() {
thread = new Thread(this, "Game");
thread.start();
running = true;
init();
}
public void init() {
Data.setValue("ScreenWidth", getWidth());
Data.setValue("ScreenHeight", getHeight());
addKeyListener(this);
}
public void update(double delta) {
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if(bs == null) {
createBufferStrategy(2);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.white);
g2.fillRect(0, 0, getWidth(), getHeight());
gsm.draw(g2);
g2.dispose();
bs.show();
}
public void run() {
long lastLoopTime = System.nanoTime();
long lastFpsTime = 0;
final int TARGET_FPS = Fps.TargetFPS;
final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
int CurrentFps = 0;
while(running) {
long now = System.nanoTime();
long updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ((double) OPTIMAL_TIME);
lastFpsTime += updateLength;
CurrentFps++;
if (lastFpsTime >= 1000000000) {
Fps.FPS = CurrentFps;
lastFpsTime = 0;
CurrentFps = 0;
}
update(delta);
render();
try {
if(((lastLoopTime-System.nanoTime() + OPTIMAL_TIME) / 1000000) >= 1) {
Thread.sleep((lastLoopTime-System.nanoTime() + OPTIMAL_TIME) / 1000000);
}
}catch(InterruptedException IE) {
}
}
}
public void mouseDragged(MouseEvent me) {
}
public void mouseMoved(MouseEvent me) {
}
public void mouseClicked(MouseEvent me) {
}
public void mouseEntered(MouseEvent me) {
}
public void mouseExited(MouseEvent me) {
}
public void mousePressed(MouseEvent me) {
}
public void mouseReleased(MouseEvent me) {
}
public void keyPressed(KeyEvent ke) {
gsm.keyPressed(ke.getKeyCode());
}
public void keyReleased(KeyEvent ke) {
}
public void keyTyped(KeyEvent ke) {
}
}
I called getWindow from this class:
public class LoginState extends GameState {
private GameStateManager gsm;
public LoginState(GameStateManager gsm) {
this.gsm = gsm;
}
private JTextField username;
private JTextField password;
public void init() {
username = new JTextField(25);
username.setVisible(true);
username.setBounds(20, 20, 50, 50);
Main.getWindow().add(username);
}
public void draw(Graphics2D g) {
g.setColor(Color.gray);
//g.fillRect(0, 0, Data.getIntegerValue("ScreenWidth"), Data.getIntegerValue("ScreenHeight"));
}
GameStateManager:
public class GameStateManager {
private ArrayList<GameState> gameStates;
public static final int LOGINSTATE = 0;
public static final int MENUSTATE = 1;
public static final int PLAYSTATE = 2;
private static int currentState;
public GameStateManager() {
gameStates = new ArrayList<GameState>();
currentState = LOGINSTATE;
gameStates.add(new LoginState(this));
gameStates.add(new MenuState(this));
gameStates.add(new PlayState(this));
gameStates.get(currentState).init();
}
Please help.
Thanks for the update, but... you still haven't shown where LoginWindow is being initialized.
A guess -- you're program is starting from a different main method from the one you're showing, and so the main method which creates and assigns your JFrame to the static window field is never called. I suggest that you avoid using static methods and fields in this way, that depend on a main method to initialize. Java is an OOP language -- so make your code OOP compliant and create needed objects within code guaranteed to be called, and then assign them to non-static fields.
edit:
simply swap the two lines:
GameEngine game = new GameEngine();
window = new JFrame();
to
window = new JFrame();
GameEngine game = new GameEngine();
Your JFrame gets initialized in the main method of Main. You are better off using a static initialization block instead.
static {
window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setTitle(title + Version.newVersion());
window.setResizable(false);
window.add(game);
window.setSize(1000, 720);
window.setLocationRelativeTo(null);
window.setVisible(true);
}

Fluent animation in Swing

I'd like to create a fluent 60fps animation. The animation itself seems to be approximately at 10fps, even though my render thread calls the run method 60 times a second. I'm pretty sure the problem is that the Swing was not created with animations in mind. So what is the easiest way to solve this? Thanks a lot
Code:
import aurelienribon.tweenengine.Tween;
import aurelienribon.tweenengine.TweenManager;
import object.Text;
import java.awt.*;
public class Main implements RenderThread.RunnerIface {
private final Frame mFrame;
private RenderThread mRenderThread;
private TweenManager mTweenManager;
private Text mText = new Text("text", 250, 250);
public Main() {
mFrame = new Frame();
mRenderThread = new RenderThread(this);
mTweenManager = new TweenManager();
Tween.registerAccessor(Text.class, new TextAccessor());
new Thread(mRenderThread).start();
this.animateRight();
}
private void animateLeft() {
Tween.to(mText, -1, 1000.0f)
.target(100, 250)
.start(mTweenManager);
}
private void animateRight() {
Tween.to(mText, -1, 10000.0f)
.target(400, 250)
.start(mTweenManager);
}
public static void main(String[] args) {
new Main();
}
#Override
public void run(float delta) {
mTweenManager.update(delta);
System.out.format("Delta: %f, FPS: %f\n", delta, 1000/delta);
Graphics g = mFrame.getCanvas().getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, mFrame.getCanvas().getWidth(), mFrame.getCanvas().getHeight());
mText.render(g);
}
}
Edit:
Second attempt:
public class DrawPanel extends JPanel implements ActionListener {
private Timer tm = new Timer(30, this);
JLabel label = new JLabel("Here is my label");
private int x = 0, velX = 2;
private long time;
public DrawPanel() {
super();
// this.setLayout(null);
this.x = this.getWidth();
this.label.setBounds(x,100,100,100);
this.add(label);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
/* g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight());*/
// x=this.getWidth();
time = System.currentTimeMillis();
tm.start();
}
#Override
public void actionPerformed(ActionEvent e) {
// x+=1;
int l = (int) ((System.currentTimeMillis() - time)*0.1);
this.time = System.currentTimeMillis();
System.out.println(l);
x=x+l;
this.label.setBounds(x,100,100,100);
this.repaint();
}
}
public class Main {
private Frame mFrame;
private DrawPanel drawPanel;
public Main() {
mFrame = new Frame();
drawPanel = new DrawPanel();
mFrame.add(drawPanel);
}
public static void main(String[] args) {
new Main();
}
}
So I completely rewrote my code following MadProgrammer's advices. Now I use swing timer instead of my render thread. Result is still the same, the animation is dropping frames and it's unusable. I'm confident that the actionPerformed method is called as frequently as it should be. I guess the problem is somehow hidden in the repaint method. I tried to call the repaint method on the JFrame but it didn't help.
I was following this tutorial, but everythink seems to work fine in the creator's case.
Thanks
Due to the performance problems with Swing I decided to use JavaFX instead. Since I've created quite a lot of code in Swing I didn't want to toss it all away. Fortunately there is JFXPanel for that.
Here is the code with animation example:
public class AnimationPanel extends JFXPanel {
private Text[] nodes = new Text[1];
public AnimationPanel() {
super();
Text t = new Text(10,50, "This is test");
t.setFill(Color.RED);
nodes[0] = t;
final Scene scene = new Scene(new Group(nodes), 800, 600, Color.BLACK);
this.setScene(scene);
new AnimationTimer() {
public int x = 0;
#Override
public void handle(long now) {
for (int i=0; i<1; i++) {
final Node node = nodes[i];
node.setTranslateX(x);
x++;
}
}
}.start();
}
}
You can use this panel as a regular Swing panel.

Where should I place a new mouseListener in the main method to animate CarShape left or right when the mouse is clicked?

Below is my code. I'm new to java.swing and java.awt. Do I need to put in a new action listener as well as a mouseListener? Because the shape is moving I'm not exactly sure how to set this code up.
public class Animation {
private static boolean goingRight = true;
private static int posRight;
public static void main(String[] args) {
JFrame frame = new JFrame();
final MoveableShape shape = new CarShape(0, 0, CAR_WIDTH);
ShapeIcon icon = new ShapeIcon(shape, ICON_WIDTH, ICON_HEIGHT);
final JLabel label = new JLabel(icon);
frame.setLayout(new FlowLayout());
frame.add(label);
frame.pack();
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent event) {
if (posRight <= 0) {
goingRight = true;
}
if (posRight >= 300) {
goingRight = false;
}
if (goingRight) {
shape.translate(1, 0);
label.repaint();
posRight += 1;
} else {
shape.translate(-1, 0);
label.repaint();
posRight -= 1;
}
}
};
final int DELAY = 100;
Timer time = new Timer(DELAY, taskPerformer);
time.start();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static final int ICON_WIDTH = 400;
private static final int ICON_HEIGHT = 100;
private static final int CAR_WIDTH = 100;
}
Could you include the code for the MoveableShape and ShapeIcon? This makes it easier to run your code and see what it is doing.
If you want to change the direction of the car when it is clicked, you could add a mouse listener to the label that contains the car shaped icon:
label.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(final MouseEvent e) {
// Change the direction here...
}
});
The car should now change direction whenever you click on the label, which is the rectangle surrounding the car.
(In the example above, I've used a MouseAdapter which allows you to only implement the methods of the MouseListener interface that you need, the others do nothing.)

Paint method gets called but doesn't redraw

The problem here is that the paintComponent() method gets called, it gets the necessary variables for fillRect() but doesn't actually draw anything after a key is pressed. I don't understand why since the returned value of mato.getPositionX() does get incremented every time the D key is pressed and the incremented value gets passed to fillRect(). Here's the code:
Screen class
public class Screen extends JFrame implements KeyListener {
private Mato mDrawScreensMato;
public Screen() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
setLocationRelativeTo(null);
setSize(400, 400);
DrawScreen screen = new DrawScreen();
mDrawScreensMato = screen.getMato();
addKeyListener(this);
add(screen);
}
//keyTyped
#Override
public void keyPressed(KeyEvent ke) {
int c = ke.getKeyCode();
if (c == KeyEvent.VK_D) {
mDrawScreensMato.setPositionX(mDrawScreensMato.getPositionX() + 1);
repaint();
}
}
//keyReleased
}
DrawScreen class
public class DrawScreen extends JPanel {
private Mato mato;
public DrawScreen() {
mato = new Mato();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
System.out.println(mato.getPositionX());
g2d.fillRect(
mato.getPositionX(), mato.getPositionY(),
mato.MATO_WIDTH, mato.MATO_HEIGHT
);
}
public Mato getMato() {
return mato;
}
}
Mato class
public class Mato {
final int MATO_WIDTH = 20;
final int MATO_HEIGHT = 20;
final int MATO_START_POS_X = 20;
final int MATO_START_POS_Y = 40;
private int positionX;
private int positionY;
public Mato(){
positionX = MATO_START_POS_X;
positionY = MATO_START_POS_Y;
}
public void setPositionX(int positionX) {
this.positionX = positionX;
}
public int getPositionX() {
return positionX;
}
//Get/Set positionY
}
The main cause of your problem is you are calling setVisible to early...
The general rule of thumb is, call setVisible only after you have prepared the UI
public Screen() {
DrawScreen screen = new DrawScreen();
mDrawScreensMato = screen.getMato();
addKeyListener(this);
add(screen);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// This is a useless call as DrawScreen
// does not provide appropriate sizing hints to the layout manager
pack();
setSize(400, 400);
// This needs to be called AFTER the size of window has been determined,
// as it uses the size of the window to determine it's location
setLocationRelativeTo(null);
setVisible(true);
}
KeyListener is notoriously troublesome, you would better of using Key Bindings

How to set specific window (frame) size in java swing?

My code does not work:
JFrame frame = new JFrame("mull");
mull panel = new mull();
// add panel to the center of window
frame.getContentPane().add("Center", panel);
frame.setSize(500, 300); // << not working!!!
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack(); // give a suitable size to window automatically
frame.setVisible(true); // make window visible
I am getting very small window. How to fix it?
Well, you are using both frame.setSize() and frame.pack().
You should use one of them at one time.
Using setSize() you can give the size of frame you want but if you use pack(), it will automatically change the size of the frames according to the size of components in it. It will not consider the size you have mentioned earlier.
Try removing frame.pack() from your code or putting it before setting size and then run it.
Most layout managers work best with a component's preferredSize, and most GUI's are best off allowing the components they contain to set their own preferredSizes based on their content or properties. To use these layout managers to their best advantage, do call pack() on your top level containers such as your JFrames before making them visible as this will tell these managers to do their actions -- to layout their components.
Often when I've needed to play a more direct role in setting the size of one of my components, I'll override getPreferredSize and have it return a Dimension that is larger than the super.preferredSize (or if not then it returns the super's value).
For example, here's a small drag-a-rectangle app that I created for another question on this site:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MoveRect extends JPanel {
private static final int RECT_W = 90;
private static final int RECT_H = 70;
private static final int PREF_W = 600;
private static final int PREF_H = 300;
private static final Color DRAW_RECT_COLOR = Color.black;
private static final Color DRAG_RECT_COLOR = new Color(180, 200, 255);
private Rectangle rect = new Rectangle(25, 25, RECT_W, RECT_H);
private boolean dragging = false;
private int deltaX = 0;
private int deltaY = 0;
public MoveRect() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (rect != null) {
Color c = dragging ? DRAG_RECT_COLOR : DRAW_RECT_COLOR;
g.setColor(c);
Graphics2D g2 = (Graphics2D) g;
g2.draw(rect);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
Point mousePoint = e.getPoint();
if (rect.contains(mousePoint)) {
dragging = true;
deltaX = rect.x - mousePoint.x;
deltaY = rect.y - mousePoint.y;
}
}
#Override
public void mouseReleased(MouseEvent e) {
dragging = false;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
Point p2 = e.getPoint();
if (dragging) {
int x = p2.x + deltaX;
int y = p2.y + deltaY;
rect = new Rectangle(x, y, RECT_W, RECT_H);
MoveRect.this.repaint();
}
}
}
private static void createAndShowGui() {
MoveRect mainPanel = new MoveRect();
JFrame frame = new JFrame("MoveRect");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Note that my main class is a JPanel, and that I override JPanel's getPreferredSize:
public class MoveRect extends JPanel {
//.... deleted constants
private static final int PREF_W = 600;
private static final int PREF_H = 300;
//.... deleted fields and constants
//... deleted methods and constructors
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
Also note that when I display my GUI, I place it into a JFrame, call pack(); on the JFrame, set its position, and then call setVisible(true); on my JFrame:
private static void createAndShowGui() {
MoveRect mainPanel = new MoveRect();
JFrame frame = new JFrame("MoveRect");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Try this, but you can adjust frame size with bounds and edit title.
package co.form.Try;
import javax.swing.JFrame;
public class Form {
public static void main(String[] args) {
JFrame obj =new JFrame();
obj.setBounds(10,10,700,600);
obj.setTitle("Application Form");
obj.setResizable(false);
obj.setVisible(true);
obj.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Categories