Why isn't my JFrame repainting like I told it to? - java

When instantiating my modified JPanel class, I pass in a file through the constructor. The file (XML) gets read and the data gets used by me later in the paint method. However, right after I parse the data, I call both repaint() and revalidate(), but my GUI looks exactly the same. I call these methods both in my main class which extends JFrame and my panel class which extends JPanel.
When I choose an xml file from my JFileChooser, the drawPanel class gets instantiated with its other constructor, taking in the file and parsing it, then calling repaint and revalidate. I omitted most of the code to save your time.
Here's the main class' code:
public class myCode extends JFrame {
public myCode() {
super("Roadway Simulator");
setSize(800, 600);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(null);
drawie = new drawPanel();
drawie.setSize(new Dimension(width - 200, height));
drawie.setMinimumSize(new Dimension(width - 200, height));
drawie.setMaximumSize(new Dimension(width - 200, height));
drawie.setLocation(0, 0);
add(drawie);
setVisible(true);
try{Thread.sleep(500);revalidate();repaint();}catch(InterruptedException eeee){}
}
public static void main(String[] args){new myCode();}
}
Here's my drawPanel class:
class drawPanel extends JPanel {
boolean drawRoad = false;
public drawPanel() {
super();
}
public drawPanel(Document doc){
super();
//the change in my paint method
drawRoad = true;
revalidate();
repaint();
}
public paint(Graphics g){
super.paint(g);
if(drawRoad){
g.setColor(Color.BLACK);
g.fillRect(0,0,600,600);
}
}
}
My code is the same as above, just with a lot more detail. Why isn't my JFrame repainting?

Here:
try{Thread.sleep(500);revalidate();repaint();}catch(InterruptedException eeee){}
Understand what Thread.sleep(...) does to a Swing GUI when called on the Swing event thread -- it puts the current thread which happens to be the Swing event thread, the one responsible for all drawing and user interaction, to sleep. In other words, you put your entire application completely to sleep.
Solution -- don't call this ever on the event thread.
As an aside, there's no cost to putting each method call on its own line, and no reason for that long line that you've posted as it serves no purpose other than to confuse.

try{Thread.sleep(500);revalidate();repaint();}catch(InterruptedException eeee){} is most likely blocking the Event Dispatching Thread, preventing from processing the Event Queue and making it look like your program has hung.
See Concurrency in Swing for more details...
It is also not recommended to override paint of Swing components and instead use paintComponent, see Performing Custom Painting for more details
In your case, I would recommend using a javax.swing.Timer instead of Thread.sleep, see How to use Swing Timers for more details
Updated
I don't see any where in your code that would change drawRoad from false to true, so your paint method is painting...nothing...so I guess you frame is painting exactly the way you told it to...
You may also like to take a look at Initial Threads and you might like to have a read through Code Conventions for the Java TM Programming Language, it will make it easier for people to read your code and for you to read others
Updated
Given that you example is incomplete and won't compile, when I rebuild it, this will work...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.w3c.dom.Document;
public class TestDraw extends JFrame {
public TestDraw() {
super("Roadway Simulator");
setSize(800, 600);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawPanel drawie = new DrawPanel(null);
add(drawie);
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) {
ex.printStackTrace();
}
new TestDraw();
}
});
}
class DrawPanel extends JPanel {
boolean drawRoad = false;
public DrawPanel() {
super();
}
public DrawPanel(Document doc) {
super();
drawRoad = true;
revalidate();
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (drawRoad) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
}
}
If I change DrawPanel drawie = new DrawPanel(null); to DrawPanel drawie = new DrawPanel(); it still paints, but doesn't perform your custom painting.
The other problem is, as has already been highlighted, is the use of null layouts
Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify.
Have a look at Why is it frowned upon to use a null layout in SWING? for more details...
Now, having said that, when you add drawie, you never give it a size, Swing is smart enough not to paint 0x0 sized components...

Related

Using Graphics to display shapes on a JFrame

I am learning how to program a graphical user interface in Java. I pretty much know some basics but in this program, I am trying to draw onto a JFrame with a black background, but as soon as I run the program the JFrame only displays a white line on a white background. I would appreciate it very much if anyone knew how to fix this, I have been trying myself but I can't seem to figure it out.
Thanks for your attention. I’m looking forward to a reply.
public class test1 {
public static void main (String[] args)
{
JFrame frame = new JFrame();
frame.setSize(1835,1019);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setResizable(false);
frame.getContentPane().setBackground(Color.BLACK);
JPanel raum = new JPanel()
{
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.WHITE);
g2.drawLine(500,500,500,800);
}
};
frame.add(raum);
}
}
There a number of issues which are going to cause you endless amount of problems going into the future.
The obvious one is the fact that the background color of the panel is very close to WHITE, so it makes it very difficult to see the line. You could change the background color of the panel or the line and it should solve the immediate issue.
You really need to take a look at Performing Custom Painting and Painting in AWT and Swing to get a better understanding of how painting works in Swing.
It is generally recommended to override paintComponent and avoid overriding paint. paint does a lot work and unless you're willing to take over ALL it's workload, you're better off avoiding it.
As a general rule, you should also call the super.paintXxx method before you do any custom painting. Again, painting is generally a complex workflow, best to just let the parent class do its job.
A component should also provide sizing hints back to the parent container, the parent container can then make better decisions (via the LayoutManager) as to how all the components should be laid out. Because different platforms (and even same platforms with different settings) can generate different size window decorations, you're better off managing the size of the "content" over the size of the "window". Again, this is going to save you no end of headaches into the future.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBackground(Color.BLACK);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(1080, 1920);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setPaint(Color.WHITE);
g2d.drawLine(500, 500, 500, 800);
g2d.dispose();
}
}
}

Why doesn't repaint() work when called by a method of an object of the same class?

In a program I'm building for my class I have the same class extending a Swing JPanel and implementing MouseListener, for which I use two instantiations - one to function as a JPanel, and the other as a mouse listener for that JPanel.
But when I click in the window, repaint() the MouseClicked method in the mouse listener fails to call the first object's paintComponent() method. For example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TestPanel extends JPanel implements MouseListener{
static boolean black;
static TestPanel test = new TestPanel();
public void mouseExited(MouseEvent e){}
public void mousePressed(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseClicked(MouseEvent e){ //Expected behavior: the square turns black immediately
System.out.println("CLICK!");
black = true;
test.repaint(); //this fails
try{
Thread.sleep(3000);
}catch(Exception ex){}
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
System.out.println("Painting...");
g2d.setColor(Color.white);
if(black){
g2d.setColor(Color.black);
}
g2d.fillRect(0, 0, 200, 200);
}
public static void main(String[] args) throws InterruptedException{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.addMouseListener(new TestPanel());
test.setPreferredSize(new Dimension(200, 200));
frame.add(test);
frame.pack();
frame.setVisible(true);
while (true){
black = false;
test.repaint();
Thread.sleep(100);
}
}
}
If you watch what happens on a click, the screen stays white for the 3 seconds after the click is registered, until the loop starts up again, i.e., the repaint() call in the mouse listener didn't work. Why does this happen?
I'm guessing it would work if I made different classes for the objects, but I'm mostly curious as to why it doesn't work this way.
for which I use two instantiations - one to function as a JPanel, and the other as a mouse listener for that JPanel.
There is no need to do that. All you need is a single instance of the TestPanel class.
In the constructor of your TestPanel class you just add:
addMouseListener( this);
The get rid of the static variable for the TestPanel class.
Then the code in your main method should look something like:
//test.addMouseListener(new TestPanel());
//test.setPreferredSize(new Dimension(200, 200));
//frame.add(test);
frame.add( new TestPanel() );
Also, the TestPanel class should override the getPreferredSize() method to return the Dimension of your panel.
Read the section from the Swing tutorial on Custom Painting for a working example with a MouseListener.
The AWT thread is responsible for calling MouseListener and for repaint.
Inside the repaint(); method, the AWT thread is told to call the paint();
Just call it using a different thread. In general, it is a bad idea to do anything intensive with the AWT thread. It already does a lot, taking too much of its time will mess your GUI up.
Depending on your needs, this might work:
new Thread(()->{repaint();}).start();

Java Program flow

i have been programming in python for a while and have been interested in learning Java for a while now so i figured i would give it a go so have mercy i know this is a infinite loop at the moment so tread carefully.
import javax.swing.*;
import java.awt.*;
public class snake extends JFrame{
public static void main(String[] args) {
JFrame screen = new JFrame("Snake");
screen.setSize(640, 480);
screen.setResizable(false);
screen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container pane = screen.getContentPane();
screen.setVisible(true);
while(true) {
pane.add(new Render());
}
}
}
class Render extends JComponent {
Render(){
}
public void paint(Graphics g) {
g.setColor(Color.white);
g.fillRect(0,0,640,480);
g.setColor(Color.green);
g.fillRect(100,100,25,25);
}
}
my current goal for this small program is to loop my code so that every time my while loop iterates i would like the flow of code to update the screen and redraw anything so in the future the rectangle could move. From my tests i have been running the first iteration of the loop runs through the method inside of my Render class and then after that it stays in the (i believe that is a constructor in java)
Render() {
}
piece of code.where it cycles through every iteration how i would like it for the method. I have tried to call the method from inside the constructor and that did not work. How can i route the flow of the program so every iteration of my loop the program goes through that method? Thanks for your time.
Start by taking a look at Painting in AWT and Swing and Performing Custom Painting for more details about how painting in Swing works...
This...
while(true) {
pane.add(new Render());
}
is going to cause issues. If it doesn't block the Event Dispatching Thread, it will run so fast as it cause other threads to stall (or run more slowly) and simply continuously add new instances of Render to pane, which I'm pretty sure you don't want.
It is generally encouraged to override paintComponent instead of paint, as it can cause less issues. You should also, be convention, call super.paintComponent before doing any custom painting.
Instead, simply create an instance of Render and add it to the frame
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Snake {
public static void main(String[] args) {
new Snake();
}
public Snake() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Render());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
class Render extends JComponent {
Render() {
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.white);
g.fillRect(0, 0, 640, 480);
g.setColor(Color.green);
g.fillRect(100, 100, 25, 25);
}
}
}
You are likely going to want to perform some animation, take a look at Concurrency in Swing and How to use Swing Timers for more details
For example

Java Changing Content Pane

I have recently been making a game and came across a problem I could not solve. My problem is with removing the content pane of a JFrame and setting as something else. While this works, the KeyListener in the class of the content pane does not work unless I change the primary window on the computer to something else then back to the JFrame.
I replicated the problem in a smaller amount of code than what is was originally:
import java.awt.*;
import java.awt.event.*;
import javax.awt.swing.*;
public class TheFrame extends JFrame{
private JButton play;
private FirstPanel fp;
private SecondPanel sp;
public TheFrame(){
setSize(800, 600);
setLocationRelativeTo(null);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
fp = new FirstPanel();
setContentPane(fp);
setVisible(true);
}
public static void main(String args[]){
TheFrame tf = new TheFrame();
}
class FirstPanel() extends JPanel{
private boolean test = false;
public FirstPanel(){
play = new JButton("play");
play.addActionListener(new PlayListener());
add(play);
}
public void paintComponent(Graphics g){
if(test == true){
sp = new SecondPanel();
removeAll();
revalidate();
setContentPane(sp);
}
}
class PlayListener implements ActionListener{
public void actionPerformed(ActionEvent e){
test = true;
repaint();
}
}
}
}
Here is also the code for the class SecondPanel:
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;
public class SecondPanel extends JPanel implements KeyListener{
private int draw = 0;
public SecondPanel(){
addKeyListener(this);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawString("press f to draw circles", 90, 40);
if(draw > 0){
for(int i = 0; i < draw; i++){
g.drawOval((i*100)+100, (i*100)+100, 100, 100);
}
}
}
public void keyTyped(KeyEvent e){
if(e.getKeyChar() == 'f' || e.getKeyChar() == 'F'){
draw++;
repaint();
}
}
}
So before anything else, this...
public void paintComponent(Graphics g){
if(test == true){
sp = new SecondPanel();
removeAll();
revalidate();
setContentPane(sp);
}
}
This incredibly bad! First, you are breaking the paint chain (not calling super.paintComponent) and secondly, you are changing the state of the component from within the paint cycle, this will trigger a new repaint request and will call your paintComponent again and again and again and again ....
Painting is for painting the current state of the component, nothing more. NEVER change the state of any component from within ANY paint method EVER
Instead of trying to use remove/add, consider using a CardLayout instead, see How to Use CardLayout. This will allow you to switch between the first and second panels based on your needs, from a centralised control point.
KeyListener is a fickle mistress, it wants all the attention, all of the time. It will only raise key events if the component it is registered to is focusable AND has focus. A better solution is to use the key bindings API, which has been designed to overcome this limitation and provide you with a level of control over the level of focus required to trigger the associated actions.
See How to Use Key Bindings for more details
To swap content of a container, be it a JFrame's contentPane or any JPanel, consider using a CardLayout since this tool was built specifically for this job.
Note that this code:
sp = new SecondPanel();
removeAll();
revalidate();
setContentPane(sp);
should never be found inside of a paintComponent method. This method is not under our direct control, and should be for painting and painting only. Also by not calling the super's method, you have broken the painting chain.
Also, instead of KeyListeners, use Key Bindings, and your functionality should work.
For instance, please have a look at the similar CardLayout code I created today for another similar question.

Java animation freeze

I am trying to write a simple 2d animation engine in Java for visualizing later programming projects. However, I am having problems with the window refresh. On running, the frame will sometimes display a blank panel instead of the desired image. This begins with a few frames at a time at apparently random intervals, worsening as the program continues to run until the actual image only occasionally blinks into view. The code for processing each frame is run, but nothing in the frame is actually displayed. I believe the problem may come from my computer more than my code (certainly not from bad specs though), but am not sure. Help much appreciated.
Three classes. Code here:
package animator;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.applet.AudioClip;
public class APanel extends JPanel
{
public APanel(int l, int h){
setPreferredSize(new Dimension(l,h));
setLocation(80, 80);
setVisible(true);
setFocusable(true);
}
public Graphics renderFrame(Graphics g){
return g;
}
public void paintComponent(Graphics g) {
requestFocusInWindow();
renderFrame(g);
}
}
package animator;
import java.awt.*;
public class Animator extends APanel
//extending the APanel class allows you to code for different animations
//while leaving the basic functional animator untouched
{
public static final int SCREEN_X = 700;
public static final int SCREEN_Y = 700;
int frameNum;
public Animator() {
super(SCREEN_X, SCREEN_Y);
frameNum = 0;
}
public Graphics renderFrame(Graphics g) {
frameNum++;
g.drawString(""+frameNum,5,12);
return g;
}
}
package animator;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.Timer;
public class Runner {
int framerate = 30;
Animator a = new Animator();
JFrame j = new JFrame();
public Runner(){
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
j.add(a);
start();
j.setSize(a.getPreferredSize());
j.setVisible(true);
}
public void start() {
Timer t = new Timer(1000/framerate, new ActionListener() {
public void actionPerformed(ActionEvent e){
j.getComponent(0).paint(j.getComponent(0).getGraphics());
//The following line of code keeps the window locked to a preferred size
// j.setSize(j.getComponent(0).getPreferredSize());
}
});
t.start();
}
public static void main(String[] args){
Runner r = new Runner();
}
}
There are some serious mistakes in your code which could be the cause or a factor of your problem...
j.setSize(a.getPreferredSize()); is irrelevant, simply use JFrame#pack, you get better results as it takes into account the frame decorations
j.setSize(j.getComponent(0).getPreferredSize()); use JFrame#setResizable and pass it false instead...
NEVER do j.getComponent(0).paint(j.getComponent(0).getGraphics()); this! You are not responsible for the painting of components within Swing, that's the decision of the RepaintManager. Just call j.repaint()
super(SCREEN_X, SCREEN_Y);...just override the getPreferredSize method and return the size you want.
setLocation(80, 80); irrelevant, as the component is under the control of a layout manager
setVisible(true); (inside APanel)...Swing components are already visible by default, with the exception of windows and JInternalFrames
And finally...
public void paintComponent(Graphics g) {
requestFocusInWindow();
renderFrame(g);
}
There is never any need for this method to be made public, you NEVER want someone to be able to call it, this will break the paint chain and could have serious ramifications in the ability for the component to paint itself properly.
You MUST call super.paintComponent before performing any custom painting, otherwise you could end up with all sorts of wonderful paint artifacts and issues
Never modify the state of a component from within a paint method...while it "might" not be an immediate issue calling requestFocusInWindow(); within your paintComponent could have side effects you don't know about...
Instead, it should look more like...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
renderFrame(g);
}

Categories

Resources