basically, I have a program that has a class to create a basic GUI, and another class that extends Canvas. This Canvas class is added to a JPanel in the usual fashion myPanel.add(object). Now, in this class, I want to have methods to move objects such as rectangles.
My questions are these; is there a way to essentially have this JPanel or its added object (myCanvas) running on a separate thread?
Also, in the following method:
public void paint(Graphics g){
g.setColor(Color.black);
g.drawRect(0, 0, 50, 50);
}
Is there a way to have these operations split into multiple methods? I.e. multiple methods that draw to the Canvas?
Thanks in advance.
Don't use a Canvas is a Swing applicaton. Use a JPanel or JComponent and override the paintComponent() method. Also don't forget the super.paintComponent(g) at the start of the method.
See the section in the Swing tutorial on Performing Custom Painting for more information.
Store a list of drawable objects somewhere (perhaps your Canvas class, but I'd advise that being external to your logic...) and use your other thread(s) to update this list of objects.
Your drawing loop can simply clear your canvas (or at least areas that need to be redrawn) and draw those. Ideally your screen-render should be fast enough to facilitate a complete redraw, caching sub-sections as necessary.
Related
so when i try to create a paint method i.e.
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
}
my JFrame window will go completely black when I run my program. If I remove the paint method from the code, my normal images will appear in my JFrame window. I know FOR SURE that I am not using a .paint() method anywhere else in my code because I just added this method to my code now. Does anybody know why this is happening?
update, I am using eclipse
Start by taking a look at Performing Custom Painting and Painting in AWT and Swing to be better understand how painting works in Swing.
Basically, the paint subsystem is calling your paint method as required, when it decides that the component needs to be updated.
Because you've overridden it to basically do nothing, then nothing is painted.
As a general piece of advice, you should avoid overriding paint of top level containers like JFrame, because they are actually composite components. That is, they are actually made of a series of layers which the UI is built on
See How to use root panes for more details, but basically...
I am doing a project with double buffering. When I paint, it simply paints on top of the old layers, but I need to erase them. Repaint() didn't work, but I'm guessing something equally as simple is the answer.
Any ideas?
Added code, and now it disappears, but it erases the background color.
public void paint(Graphics g)
{
super.paint(buffer);
for(Projectile p: projectiles)
drawRectImage(buffer, p.image, p.getRectangle());
}
Suggestions:
If this is a Swing GUI, then don't override the paint method, but instead override the paintComponent method. This won't help your current problem, but will help prevent future problems including problems with painting of borders and child components.
If Swing (again you don't say), then make sure that your painting component extends JPanel, not JComponent, since JPanel is opaque and fills its background rectangle in its super method.
If it's not Swing, then you should strongly consider changing from AWT to Swing.
If you're still stuck, then yep, you'll want to create and post a minimal example program. Please check out the link.
I have looked all round, and no one seems to have an answer for me. I have a JPanel where I add/remove labels from it while it;s running. The problem is that every time I add a new label to it I have to revalidate() it so the new label shows up. Every time I revalidate(), it I get a very annoying flicker, and all my labels ghost up to the top of my window for a millisecond then return to what they were doing. The only time I call revalidate() is after I add a label; I do not change the paint method or anything only did
public void paintComponent(Graphics page)
{
super.paintComponent(page);
}
so that my page would not look bad. I could really use some help on this. I've made a asteroids game using JFrame, Jlabel and Jpanel game; it work great, but this flickering and component "ghosting quickly ghosting to top of panel" is just unbearable, and I know for a fact this is the revalidate() method.
Using labels (or generally GUI components) to represent quickly animating, dynamically created/moved objects is not practical. Its like using a car to drive from the living room to the kitchen.
The Swing components come with a lot of nice features, e.g. they can determine the space they require and a layout manager can use that to automatically place them nicely. Unfortunately for a game, you neither need all the nice stuff, nor do you want to pay the processing overhead involved with it. Matters are further complicated by swings multi-platform capabilities which mandate that each component has a peer component (the peer deals with rendering the component to the active look).
To solve this, you do not represent individual game objects as swing components. Instead you use a single swing component (usually JPanel) as a canvas to paint your game objects on to. You simply call repaint() for that JPanel periodically (e.g. 30 or 60 times a second). Of course you will override paintComponent() on the JPanel to do paint all the game objects using your own code.
Edit: To outline how you do your own rendering
You need to decide how to organize your game related objects. A very simple skeleton could look like this (getters/setters omitted):
public class GameObject {
public int x;
public int y;
public Image image;
}
In practice this class will have additional members (like a behavior that controls how the object moves, and maybe some states/counters for animation). It may also be abstract with a concrete subclass for each possible behavior (like ship, asteroid and bullet). All instances of GameObject are managed in some kind of collection, I assume you use a List.
When implementing the paintComponent it comes down to just loop through the list and render each objects's image:
public void paintComponent(Graphics g) {
List<GameObject> list = ... // however you obtain it in your game
for (GameObject gobject : list) {
g.drawImage(gobject.image, gobject.x, gobject.y, (ImageObserver) null);
}
}
More elaborate games may order the gameobjects to get control of how game objects overlap each other. But in principle its incredibly simple.
When I was having problems with filckering Swing components it turned out to be concurrency issues. Since you are doing a game, which means you are doing animation it may be the case here.
Take care to modify Swing components only from AWT-EVENT thread. Either use SwingUtilities.invokeLater or do your animation using javax.swing.Timer.
As a concrete example, Asteroids doesn't flicker at all. Here are a few reasons:
It runs on the event dispatch thread.
It uses JPanel, which is double buffered by default.
It paces the animation using javax.swing.Timer, updating the game's model in actionPerformed().
It overrides paintComponent(), but can safely omit super.paintComponent() because the action listener repaints the entire panel.
The listener calls paintImmediately() to avoid any potential delay; substitute repaint() to see if there's any difference on your platform.
Addendum: As shown in this counter-example, revalidate() does not cause flickering. Instead of replacing labels, update the label's icon, as shown here.
Have you considered reusing them - i.e. using setVisible(false) when they're not needed?
Not sure if adding and removing labels is the best way of achieving what you want for a game.
I'd personally be tempted to manage the drawing myself.
I'm trying to implement a simple window scale in java's swing library. The goal is simply to double the window height and width, and paint the window and each of its components in scale.
Here's an example of the code I'm using:
public class MyWindow extends JFrame {
...
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.scale(2,2);
super.paint(g);
}
}
The position and size for each my components in this window is set manually using setBounds with a null layout for the window.
When I actually run the program, what happens is that the first paint for the window seems successful-- everything is sized appropriately. Each subsequent repaint by the components, however, is neither twice the size, nor in the proper location. Here's what I mean:
As you can see, portions of the screen which have components that call repaint manually (the animating bits), don't seem to be using the Graphics2D scale of the JFrame. I looked in the source code, and tried overloading a few other methods (update and repaint, mostly), but all of them seemed to produce the same result. I further looked at the paint and repaint methods of the component and container classes, but they all seem to call a specified repaint of their parent. Shouldn't my Window be the "biggest" parent? If so, why haven't these repaint calls reached my Window?
My big question to you is, therefore: what repaint methods of the parent component do the child components call? Why aren't the calls properly routed to my JFrame's paint call? Is there any other (better) way that I can scale my window? Any and all help is appreciated!
As discussed in Painting in AWT and Swing: The Paint Methods, "Swing programs should override paintComponent() instead of overriding paint()." A common approach is to create a view by overriding paintComponent() in a JComponent (or subclass), as shown here. Let your view listen for changes to the game's model, as discussed here.
SwingUtilities.updateComponentTreeUI() should be used to change the Look & Feel, not update the view.
Use
javax.swing.SwingUtilities.updateComponentTreeUI(parentComponent);
when you need to make sure all of the parentComponents child component's look and feel are updated properly.
I'm trying to make a paint editor with Java in which I have a toolbar with the objects that I would like to paste in the canvas. I'm using Swing components to make the GUI, but when I looked for the way of making the canvas, I only found the class canvas from AWT.
Is there any way to make something similar to canvas with Swing? (for example, JPanel?) I have read that using the class canvas from AWT with a GUI made with swing won't work correctly, is that true?
In order to make a custom 'Canvas' in swing you usually write a subclass of a JPanel. Then, you must overwrite the protected paintComponent(Graphics g) method of JPanel.
In the paint method, you can call methods on the Graphics object to actually draw on the JPanel.
As always, the Java Tutorials have a great reference on this to get you started.
You'll probably want to make a subclass of JPanel and implement your own way of painting components you want to draw onto the panel.
The basic approach will probably be along the line of assigning a MouseListener to the subclass of JPanel, then implement painting functionality.
The basic idea may be something along the line of:
class MyCanvas extends JPanel implements MouseListener
{
Image img; // Contains the image to draw on MyCanvas
public MyCanvas()
{
// Initialize img here.
this.addMouseListener(this);
}
public void paintComponent(Graphics g)
{
// Draws the image to the canvas
g.drawImage(img, 0, 0, null);
}
public void mouseClicked(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
Graphics g = img.getGraphics();
g.fillOval(x, y, 3, 3);
g.dispose();
}
// ... other MouseListener methods ... //
}
The above example is incomplete (and not tested -- it definitely won't compile), but it gives an idea about how to implement a MyCanvas class in which a user can click on and draw circles.
The img object is used to hold the image of the canvas. The paintComponent method is used to paint the img object to the canvas. In the mouseClicked method, the Graphics object associated with img is retrieved in order to fillOval onto the image.
Since one the requirements is to paste images onto the canvas, it may be a good idea to hold some Images that you want to paste into the canvas. Perhaps something along the line of:
Image[] myImages; // Used to store images to paint to screen.
Then, in the routine to paint the image onto img stored in MyCanvas:
g.drawImage(myImage[INDEX_OF_DESIRED_IMAGE], 0, 0, null);
By using the drawImage method of the Graphics object, other Images can be drawn onto Images.
As for the question on AWT and Swing, yes, it is true that you do not want to mix components from the AWT and Swing, as they differ in the way they render GUI components. AWT is based on heavyweight components, meaning they native windowing for painting the GUI, while Swing is based on lightweight components, meaning the GUI is drawn by Java itself without using native components.
A good guide on the difference of AWT and Swing is provided in Painting in AWT and Swing article from Sun.
Simply subclass JComponent.
JPanel is an inappropriate class. It is often suggested as it appears to have setOpaque(true) invoked on it automatically. It's actually the PL&F which does that, and whether or not it actually happens is implementation and vendor dependent.
Canvas is a heavyweight component. That is to say that it is controlled by the underlying windowing system. The result is that it will typically be drawn over the top of Swing components, without respect to z-order or clipping (putting it in a scroll pane will give odd behaviour).
You might want to look at the Minueto API. It is a very simple to use graphics api, and you can combine the Java event listening with it to provide your drawing capability.
http://minueto.cs.mcgill.ca/