I am trying to create a simple application that shows a red circle that when clicked displays different messages under it. I believe that this part of the code:
g.drawString("DO NOT PRESS", 100, 100);
is coded correctly but no text is displayed on the window that opens. Here is the full code so far:
import java.awt.Graphics;
import javax.swing.JFrame;
public class BigRedButton extends JFrame {
public BigRedButton() {
setTitle("Big Red Button");
setSize(500, 500);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void graphics(Graphics g) {
g.drawString("DO NOT PRESS", 100, 100);
}
public static void main(String[] args){
new BigRedButton();
}
}
There is no such method graphics in JFrame, so nothing is calling it.
You should avoid painting directly to top level containers, apart from everthing else, they're not double buffered and will flicker when painted. You should, instead, create a custom component (extending from something like JPanel) and override it's paintComponent method.
You should take the time to read through Performing Custom Painting, Painting in AWT and Swing and 2D Graphics
Also, while your reading up, you should have a read through Initial Threads
Amendment
As pointed out by Andrew, you should use the #Override annotation to ensure that the method you think you are overriding is actually the method being overridden in the first place. This would stop the program from being compiled and save lots of lost time trying to figure out why things aren't working the way you expect them.
Related
I am having trouble in understanding the difference between the paint and the paintComponent methods defined in the JPanel class. To put you in context, I am trying to draw a chess table; the following is part of the code I have been working on.
package main;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.Color;
public class Panell extends JPanel {
public Panell() {
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(100, 100, 400, 400);
}
#Override
public void paint(Graphics g){
boolean white=true;
for(int y=0;y<8;y++){
for(int x=0;x<8;x++){
if(white){
g.setColor(new Color(235, 235, 208));
}
else{
g.setColor(new Color(119, 148, 85));
}
g.fillRect(x*15, y*15, 15, 15);
white=!white;
}
white=!white;
}
}
}
One of my first questions is Are they runned automatically? Also, since they apparently do the same thing, which is to "paint", is it recommended to merge this two methods in one? Which would be the name of this one method?
I'm pretty new to Java and so any help will be very much appeciated. Thanks!
I have been following multiple tutorials online but any of them mentioned my question. I suppose it might be a really indepth topic, but I would really like to know the answer. I have tried everything that I have said in the question and I always end up painting one thing over the other, hence I asked myself if it would be better to only have one single paint method (by merging them), but by doing that, another question arises, Why would they implement another method to do exacly the same as another method?.
Are they runned automatically?
No. They are actually called by the windowing system when the Component needs to be repainted (i.e. first shown, resized, user calls repaint())
Also, since they apparently do the same thing, which is to "paint", is
it recommended to merge this two methods in one?
The difference between paint and paintComponent is that in Swing, JComponent implements paint to call paintBorder, paintChildren, and paintComponent in the proper (expected) order. So normally if you are subclassing a J-whatever in Swing, it is better to override paintComponent, not paint
I try to optimize the drawing of my application. Currently i have implemented an animation as well as several GUI components. Some are separated exactly, some overlap each other. Currently i face a problem within the overlapping of swing components. A part of the GUI, which overlaps my animation, needs to draw a lot of Strings and - into an Jlist placed - common swing components.
In result the overlapping GUI becomes redrawn as often as the animation gets an update. I tried using quite a lot of different methods to make sure, whats drawn in front of each other. Things like GlassPane, Jlayeredpane. Unfortunately in any of these tries, the overlapping Menus paintcomponent method, which only need to become called as the user interacts with them, gets called frequently due to animation and causes a quite high cpu usage.
Ive tried to position the Menus in a lower order within the Layeredpane, i.e.:
getLayeredPane().add(map, JLayeredPane.DEFAULT_LAYER);
getLayeredPane().add(mapController, JLayeredPane.PALETTE_LAYER);
getLayeredPane().add(settings, JLayeredPane.PALETTE_LAYER);
getLayeredPane().add(painter, JLayeredPane.POPUP_LAYER);
During the paint process of the painter i tried to modify the area - i.e.:
#Override
protected void paintComponent(Graphics g) {
g2 = (Graphics2D)g;
g2.setRenderingHints(DefaultResources.getRenderQuality());
g2.clip(JMain.getInstance().getMapBounds());
...}
Well - as soon as the painter component !isOpague(); All components underneath become redrawn. Unfortunately if i do place the menus in a higher order, they as well need to become redrawn with any animation update.
Does somebody has any idea, how to avoid a permanent redraw of overlapping components with an animated component?
The only solution i have seen was using heavyweight containers. Unfortunately the relative positioning has also shown a behavior during moving purposes, which aren't appropriate.
Thanks for any kind of advice!!
Well, its pretty obvious that if you have overlapping non-opaque components all of them will be repainted on any changes in one of them unless you optimize your animation repaint calls to some specific rectangles, so there won't be any useless operations.
Let me describe how Swing works a bit more - all of the paintings you do in paint, paintComponent and other methods (which get called on each component repaint) are done onto subimages of a single image that holds a "cached" version of the whole frame interface.
Now imagine that you change something in your UI (add/remove/repaint component) - that final image (or atleast a small part of it that contains your component) must be properly updated. To do that in case your component is NOT opaque - all of sub-components will be repainted with your component bounds as a repaint rect to create a proper background for your component. If your component is opaque - it will be the only one repainted, but it also has to fill the whole bounds rect on its own, otherwise you will see awful painting artifacts behind your component on each repaint.
To summ up - to avoid pointless repainting of overlapping components there are a few approaches:
Optimize animation repaint calls to areas you actually need to repaint
Use opaque components, unless you want to paint something transparent in your component
Optimize all overlapping components painting operations so repaints won't take much time
There still might be more optimization approaches depending on your specific case, but you will have to find them on your own, since that is impossible without seeing the whole picture.
You can also find a lot of useful information about optimizations in Swing in this book: Filthy Rich Clients
Well in terms of optimization i do have a component, which is causing quite some trouble, but that one i have planned to rewrite. Therefore i just want to make sure, that painting areas are properly. Following that point, i have computed all necessary areas and filled them into a list, which i pass as data has changed.
Its working properly as long as i only have one rectangle applied. As soon as i pass a second one, it seems its y - extension becomes ignored. For instance:
[x=0,y=0,width=322,height=20]
[x=0,y=620,width=322,height=20]
There everything between y=20 and y=620 also gets redrawn.
for (Rectangle rec : clippingAreas) {
painter.repaint(rec);
}
Ok i have tried paintImmediately within the EDT, which does work for now, but i wonder if this is a proper way to go:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (Rectangle rec : clippingAreas) {
painter.paintImmediately(rec);
}
}
});
Ok there we go:
package animationissue;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
public class AnimationIssue extends JFrame {
JPanel a = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("map has become repainted");
}
};
JPanel b = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("menu as well");
}
};
public AnimationIssue() {
this.setSize(500, 500);
this.setLayout(null);
a.setSize(400, 400);
b.setSize(400, 200);
this.getLayeredPane().add(a, JLayeredPane.DEFAULT_LAYER); // Map
this.getLayeredPane().add(b, JLayeredPane.PALETTE_LAYER); // Menu
a.setLocation(0, 0);
b.setLocation(0, 100);
a.setBackground(Color.red);
b.setBackground(Color.blue);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
// doin some computations for animation
// cast a repaint after having finished new
//animation information i.e. interpolation
while (true) {
try {
Thread.sleep(2000);
} catch (Exception e) {
}
// case 1 - just a repaint of the whole component - triggering map redraw results in menu redraw
// a.repaint();
// case 2 - repaint of specified rectangle
// Either passing one - the menu does not get repainted, or passing both - menu also gets repainted
//a.repaint(0, 0, 400, 100);
//a.repaint(0, 300, 400, 100);
// paintimmediately works for now
//a.paintImmediately(0, 0, 400, 100);
//a.paintImmediately(0, 300, 400, 100);
// Just repainting Menu does not trigger map to become repainted, except if its opague, but then it should become repainted
b.repaint();
}
}
});
t.start();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
AnimationIssue f = new AnimationIssue();
f.setVisible(true);
}
}
I was really looking forward to optimize the behavior, that no menu gets redrawn, if its not necessary. You have to imagine the menu of being a component holding several JLists with a lot of String drawing tasks, which really have a massive impact on cpu usage. I havent been wondering, since it gets redrawn approximately 25 times per sec.
I am just uncertain for the current solution, if using paintImmediately is proper. Apart from this - if you or somebody has an alternative - better - way to prevent useless redrawing (i really thought Glasspane or JLayeredPane or isOptimizedDrawing or isOpaque might help out), i am really thankful.
Best regards.
I'm trying to create a java game in which balls randomly drop from the top of the screen and we need to catch the balls using a catcher which is located at the bottom of the screen.
I'm having a difficult time figuring out how to actually draw this onto my JFrame.
I've got a class for my 'catcher', 'ball', 'game space' and I would like to put it all together.
How do I draw my 'catcher' onto my screen?
Currently, I have a 'Game' class which looks like this.
public class Game extends JFrame implements KeyListener {
GameScreen gameScreen;
Catcher playerOneCatcher;
public static void main (String[] args) {
new Game();
}
public Game() {
super("CATCH");
setSize(640,480);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setLocationRelativeTo(null);
setResizable(false);
addKeyListener(this);
this.gameScreen = new GameScreen();
this.playerOneCatcher = new Catcher(40, 10);
}
I've tried something like this in my Catcher class...
public void paintComponent(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(positionX, positionY, this.width, this.height);
}
However, its not showing on my screen.
Any help would be greatly appreciated.
You need to rethink your strategy here. Swing is a component framework, with most components intended for building user interfaces. These components are not optimised for what's typically required in games. You want to look into double-buffering, sprites etc. The way to go will be to read up on Graphics2D class (or abandon Swing altogether!)
However answering to your question - if Catcher is a Swing component - you need to add it to the "parent" component, e.g. like this:
this.add(playerOneCatcher);
Same goes to gameScreen but from your snippet it is not obvious what this component is. I hope this helps.
Also, check this out for some ideas: 2D Java Game. Moving sprite above tiled images
Did you call super.paintComponent (g) ? That can cause a few bugs.
Did you call invalidate () or repaint () to repaint the thing you are painting on? I hope you have a special JComponent, and you are not drawing on a JFrame. That is NOT good.
I have the following code being called:
while(true){
view.onTick();
trySleep(55);
}
The onTick() method is described as such:
public void onTick() {
mainFrame.paintAll(mainFrame.getGraphics());
}
Here is where I set up my JFrame and JPanels etc (mainFrame is a JFrame):
private void runProgramSetup(){
JPanel canvas = new JPanel();
canvas.setLayout(new BoxLayout(canvas, BoxLayout.Y_AXIS));
mainFrame.getContentPane().add(canvas);
//create the main game panel
mapPanel = new MapPanel(model, this);
mapPanel.setPreferredSize(new Dimension(TOTAL_FRAME_WIDTH, MAP_PANEL_HEIGHT));
mapPanel.setBackground(Color.cyan);
//create the menu panel
menuPanel = new MenuPanel(model, this);
menuPanel.setLayout(new BoxLayout(menuPanel, 0));
menuPanel.setPreferredSize(new Dimension(TOTAL_FRAME_WIDTH, MENUS_PANEL_HEIGHT));
//add the panels to the window
canvas.add(mapPanel);
canvas.add(menuPanel);
//make both panels visible
mapPanel.setVisible(true);
menuPanel.setVisible(true);
}
Now here is my problem. Everything repaints when repaintAll() is called EXCEPT mapPanel's overridden paint(Graphics g) method:
#Override
public void paint(Graphics g) {
transformedImages.transformAndStoreImages(model);
paintGrid(g);
paintScenery(g);
paintElements(g);
paintDraggedElement(g);
paintUIOverlay(g);
}
It is only called once. That is it. However, every other component continues to repaint. It is only mapPanel that paints once. Here is what is even more strange. I am running on Ubuntu and get this problem. The rest of my team is running on Macs and they do not have this problem. The only way I have been able to solve this is to replace onTick() with two paint calls:
public void onTick() {
mainFrame.repaint();
mainFrame.paintAll(mainFrame.getGraphics());
}
This is all that has worked for me. I need both calls. Neither works alone. I don't like doing this though obviously because of inefficiency.. :/
Any ideas?
Thanks!
you should be overriding JPanel's
paintComponent(Graphics g)
not paint
The reason mainFrame.repaint() forces the map to refresh is because repaint() calls repaint(0, 0, 0, width, height), which marks the entire mainFrame's area to be marked as "dirty" for the RepaintManager. This is designed this way on purpose, because you usually do not want to repaint every pixel in the JFrame just to update a component. So, in onTick(), when mainFrame.paintAll() is being called, my guess is that the mapPanel's area has not been marked dirty, so the RepaintManager skips it, to save processing time. If you are very sure that you want to repaint the whole mapPanel every time onTick() is called, the simplest way would be to call mapPanel.repaint() inside of your onTick() method. This will mark the whole mapPanel as dirty so it will be redrawn asap. Also, if your menuPanel is just using regular swing JComponents, you don't need to manually cause them to repaint, they will be repainted when their values change, if you are using the API correctly.
I know this is a kind of old question, but I figured I'd answer in case anyone else runs into something similar.
I'm having trouble finding out why the following problem happens: In a program that uses "extends Frame" to create a window, I can use BufferedImage to draw to the graphics context of the Frame (not JFrame), and it looks just fine. However, the moment I declare a JPanel, all of the text drawn by BufferedImage becomes obscured (not completely, but semi-transparent and hard to read), even if I don't add the JPanel to the JFrame.
Here's a simplified version of the code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.Timer;
import java.awt.image.*;
import javax.swing.*;
public class MyProgram extends Frame {
static Frame f;
static Timer timer;
public static void main(String[] args) {
f = new Frame();
f.setSize(400, 200);
f.setResizable(false);
f.setVisible(true);
f.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
drawScreen();
}
});
drawScreen();
}
public static void drawScreen() {
BufferedImage off_i = new BufferedImage(f.getWidth(), f.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = off_i.createGraphics();
g.setColor(new Color(50, 50, 50));
g.drawString("Hit any key; why does this text change?", 15, f.getHeight() - 10);
Graphics off_g = f.getGraphics();
off_g.drawImage(off_i, 0, 0, null);
JPanel panel = new JPanel();
}
}
I could maybe understand seeing the problem arise if I had added the JPanel to the JFrame and didn't set any bounds to its visibility, but even creating the JPanel gives that issue, which seems weird to me.
Basically, what I'm trying to do here is take an existing program that I have that runs just fine without JPanel, and I want to add to it a JTextArea so that I can accept copy/paste text for modifying the displaying of the program.
My understanding of Java is kind of spotty, as I learned it mainly by hobby and not formally, but I'm always looking to learn more when I can. Thanks for the help!
Update: I discovered that this problem only happens when the draw function is called again, after the JPanel has been declared, though I still don't understand why it does that or how to get around it.
better would be put Image to the JLabel and how ot use Icon
please read Using Swing Components and how to LayoutManagers works
tons examples on java2s.com
Don't mix AWT components with Swing component. That is you should use a JFrame NOT a Frame.
Don't use getGraphics(). Custom Painting is done by overriding the paintComponent() method of a JPanel (or JComponent). You just use the Graphics object that is passed to the method. Then you add the panel to the frame.
As already mentioned using a JLabel is simpler because you don't have to do any custom painting. The tutorial also has a section on "How to Use Icons".
I tried to run your code. And although the effect that you are describing does not happen on my system I can recommend you something.
First try to create your panel before it is visualized. In this case java does not have to re-arrange the components that are already on screen.
Second, if you have to draw things on visible frame call validate() of the container when you are done. This makes java to re-arrange stuff.
Third, when you are using drawXXX() methods create your own class that extends Component, JComponent, Canvas and override its `paint(Graphics) method. In this case the system will call this method every time it needs (e.g. when window is resized) and your UI will be painted again.
BTW I have 2 questions:
why are you using drawText() instead
of Label or JLabel? Use them and
avoid such kind of problems.
Why do you extend your class from Frame and do not use this fact but create yet another instance of Frame?
As an answer to my original question:
It seems that initializing JPanel alongside awt draw() commands causes the text to be antialiased, which makes the text look harder to read, partially obscured, thinner, etc. Although I tried setRenderingHint() with VALUE_TEXT_ANTIALIAS_OFF, it did not solve the problem. But as other posters pointed out it's not best practice to mix the two components.
While this doesn't exactly solve my problem, it does answer the question of what is going on, that being text antialiasing as some result of JPanel (does that sound right?). Ideally I wouldn't want to rewrite all of the code just to add a single JTextArea into an already existing codebase. But perhaps it's good every now and then to revisit old code and revamp it where it may be faulty.
Thanks everyone for the comments, information, and resource links!