why components redrawn when i override with empty paintComponent(g) method? - java

As i understand (and i am java noob), when i resize a window or change its content paintComponent() method should be called automatically. It redraws everything, so when i override it with an empty method, nothing should be redrawn...but it is. Why? Probably i am missing something. What exactly is redrawn by paintComponent(), everything? Or some backgrounds or smth?
import java.awt.*;
import javax.swing.*;
public class TextFrame extends JFrame {
public TextFrame(String text, String fontName) {
super("Show Font");
setSize(725, 150);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TextFramePanel sf = new TextFramePanel(text, fontName);
JButton ok = new JButton("i hate disappearing");
sf.add(ok);
add(sf);
setVisible(true);
}
public static void main(String[] arguments) {
if (arguments.length < 1) {
System.out.println("Usage: java TextFrame message font");
System.exit(-1);
}
TextFrame frame = new TextFrame(arguments[0], arguments[1]);
}
}
class TextFramePanel extends JPanel {
String text;
String fontName;
public TextFramePanel(String text, String fontName) {
super();
this.text = text;
this.fontName = fontName;
}
public void paintComponent(Graphics comp) {
//super.paintComponent(comp);
/*Graphics2D comp2D = (Graphics2D)comp;
comp2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font(fontName, Font.BOLD, 18);
FontMetrics metrics = getFontMetrics(font);
comp2D.setFont(font);
int x = (getSize().width - metrics.stringWidth(text)) / 2;
int y = getSize().height / 2;
comp2D.drawString(text, x, y);
System.out.println("vlad");*/
}
}

I suggest you read up on the official docs for Custom Painting: http://docs.oracle.com/javase/tutorial/uiswing/painting/index.html, more importantly under the Section A Closer Look at the Paint Mechanism.
Here's the part that's directly related to your question:
[...] the paintComponent method is where all of your painting code should be placed. It is true that this method will be invoked when it is time to paint, but painting actually begins higher up the class heirarchy, with the paint method (defined by java.awt.Component.) This method will be executed by the painting subsystem whenever you component needs to be rendered. Its signature is:
public void paint(Graphics g)
[...] The API does nothing to prevent your code from overriding paintBorder
and paintChildren, but generally speaking, there is no reason for you
to do so. For all practical purposes paintComponent will be the only
method that you will ever need to override.
So, when you're declaring:
public void paintComponent(Graphics comp) {}
you're not actually doing nothing. That's because the painting, as stated from the docs, does not begin with paintComponent(), but rather with paint(), which is called much earlier.
Now, if you declare it like this:
public void paint(Graphics g){}
then nothing will be redrawn after resizing and etc, no matter what code you have in your paintComponent(), because the beginning of the paint hierarchy has just been defined as an empty routine.

"...so when i override it with an empty method, nothing should be redrawn...but it is. Why?"
So it looks like you're running this program from the command line. If you are running the program, and it's still painting the stuff inside the paintComponent method (even after you commented out its content), most likely you ran the program with compiling again. So the program is still running from the same .class file.
When I run the program as is, I see nothing but a button, no painted words.
C:\stack>java TextFrame Hello World
" What exactly is redrawn by paintComponent(), everything? Or some backgrounds or smth?"
paintComponent just paint the component itself, which is just the JPanel in your case. It doesn't paint any other components added to it. That painting is delegated by its paintComponent method. Every JComponent has its own paintComponent method. So if you're wondering why the button still appears, it's because the call to paintComponent in the JPanel only affects the JPanel, not other child components add to it.

Related

how to call a graphical method in actionperformed?

I am kinda new to java and would like to invoke a graphical method in ActionEvent, for instance let's say I would like a square to be drawn when button b is pressed? Will appreciate any help thanks:
package Mst;
import java.awt.*;
import java.applet.Applet;
import java.awt.event.*;
public class Cours2_2 extends Applet implements ActionListener {
Button a,b,c;
public void init(){
setBackground(Color.pink);
a= new Button("KIRMIZI");
a.addActionListener(this);
add(a);
b= new Button("BEYAZ");
b.addActionListener(this);
add(b);
c= new Button("SARI");
c.addActionListener(this);
add(c);
}
public void paint(Graphics g){
g.drawString("s", 5, 5);
}
public void actionPerformed(ActionEvent e){
String s= e.getActionCommand();
if(s.equals("KIRMIZI")){
setBackground(Color.red);
}
if(s.equals("BEYAZ")){
setBackground(Color.white);
}
if(s.equals("SARI")){
setBackground(Color.yellow);
}
drawStrings(t);
}
public void drawStrings(Graphics t) {
t.setColor(Color.yellow);
t.fillRect(0, 0, 75 ,75);
}
}
I would like to know if I should create this square which I want drawn when a button is pressed as a method or a function. Thanks
Avoid Applet, if you "really" have to, use JApplet instead. Having said that, you should start with JPanel and override it's paintComponent method instead (and make sure you call super.paintComponent before doing any custom painting. Take a look at Painting in AWT and Swing and Performing Custom Painting for more details.
Generally speaking, painting in AWT/Swing is passive, that is, when the system "decides" something needs to be updated, it will then be painted. This means that you (generally) have little control over when something will be painted. You make suggestions, but it's update to the system to decide what and when something is painted.
You paint methods should paint the current state of the component. This means that you will need to provide some information and logic that the paint methods can use to make decisions about what to paint. For example, you could have a flag, which is changed by the ActionListener, which calls repaint on your component and when the component is painted, you would test the state of this flag and make decisions on what should be done (like drawing a square for example).
A more complicated approach might use a List and take advantage of the Shape API, adding or removing shapes to the List which the paint method would then be able to iterate over and paint
Have a look at Collections Trail and
2D Graphics for more details

Overriding paintComponent() Method within a custom JButton

So my ultimate goal is to change the design of a JButton from the basic looking blue button to whatever I want, like a circle.
So I create a class called "Button" and made it extend JButton
public class Button extends JButton {
public Button(String text) {
super(text);
this.setContentAreaFilled(false);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("hello");
//Paint Stuff Will Happen Here
}
#Override
public Dimension getPreferredSize() {
return (new Dimension(120, 120));
}
}
My first goal was just to make sure that the paintComponent method was being called, so I put in a debug message. That debug message has never shown.
Basically the paintComponent() method is never called, even though I'm manually calling the "repaint" method for my JFrame.
Despite the fact that the method is not being called, a regular button still shows up on my JFrame, which is really confusing to me.
Here is my JPanel code
public class Scene extends JPanel {
public Scene() {
//Initialize Listeners
Button button = new Button("Hello");
button.setBounds(400, 400, 50, 25);
this.setLayout(null);
this.add(button);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
//Paint Stuff Below
for (int xI = 0; xI < Sprite.allSprites.size(); xI++) {
Sprite sprite = Sprite.allSprites.get(xI);
if (sprite.isVisible) {
g2.drawImage(sprite.image, sprite.rawLocation.x.intValue(), sprite.rawLocation.y.intValue(), null);
}
}
g2.dispose();
}
}
Basically in my JPanel I override the paintComponent method as well so as to paint my various sprites onto the screen, which has worked just fine and is probably irrelevant to the issue.
And finally, this is my JFrame code
public class GameWindow extends JFrame {
private Scene currentScene;
public void initialize(Scene scene) {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(Settings.DEFAULT_WINDOW_SIZE);
this.setResizable(false);
this.setLocation(this.getLocationToCenterScreen());
this.setScene(scene);
}
//Gets the center of the screen with the given window
public Point getLocationToCenterScreen() {
return new Point(Settings.SCREEN_CENTER.x - (this.getSize().width / 2), Settings.SCREEN_CENTER.y - (this.getSize().height / 2));
}
public void setScene(Scene scene) {
this.currentScene = scene;
this.setContentPane(scene);
}
public Scene getCurrentScene() {
return currentScene;
}
}
Nothing really fancy in this code as far as I can tell.
I've set the content pane.
I've made sure each paintComponent() method also includes super.paintComponent(g).
I've set the LayoutManager to null for testing purposes.
I've set the Button's bounds.
As I said, the button actually does show up on the screen. But the debug message never shows.
Also, the button that shows up on the screen looks like the really old Windows buttons from 10 years ago. It's all grey with a black border.
This old button only shows up if I'm using a class that extends JButton.
Anyways, thanks guys! I hope I can get over this weird problem.
First and foremost, as a few other people have said, don't name your class "Button"; that belongs to Swing's predecessor, AWT (Advanced Windowing Toolkit), and is likely to confuse the compiler at best, and get you the wrong "Button" at worst.
That should solve the paintComponent() problem, but in addition, if all you're trying to do is change the feel of the button, then you're overprogramming this.
There are two ways to accomplish this with a JButton.
The first, and probably easiest (for an image), is AbstractButton.setIcon(Icon defaultIcon) An Icon is a type of image, loadable from a BufferedImage with ImageIcon(Image image) and manipulable in the same way. This is probably what you need.
The other method which comes to mind, which is much more broad in scope, is change the Look and Feel of the application. Most of us have several available to our systems, inclusive of the default Java look and feel, and the platform look and feel. I recommend setting it as early as possible; since it's entirely done through static methods, for small projects you might even get away with slipping it into the main method, before anything is even initialized.
Let me know if this doesn't solve your problem, and I wish you luck with the rest of your project!

Java Jpanel paint

I have this example of a java jpanel:
import java.awt.*;
import javax.swing.*;
public class JavaGame extends JFrame {
public JavaGame() {
setTitle("Game");
setSize(500,500);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint(Graphics g) {
g.drawString("Hello World!",75,75);
}
public static void main(String[] args) {
new JavaGame();
}
}
So, we just define the method paint, and we create new JavaGame object, and it just calls paint. I don't get two things:
new JavaGame() - shouldn't we assign a object name like obj = new JavaGame()?
Shouldn't we call the method paint like obj.paint()?
I have basic understanding of OOP, but this piece of code confuses me. Could someone please explain it to me?
new JavaGame() - shouldn't we assign a object name like obj = new JavaGame()?
This gets to the important distinction between objects and reference variables. What is most important here is creating a JavaGame object, which you do by new JavaGame(). Since this object displays the JFrame in its constructor, then simply creating the object is all that is needed to create the display, and you don't need to assign the object to any variable.
Note that objects don't have names, but rather, some variables do. For instance, if you created a Foo object, and assigned it to a bar variable:
Foo bar = new Foo();
But then assigned the same object to a baz variable:
Foo baz = bar;
Both bar and baz refer to the exact same Foo object. What then is the object's name? Again, object names don't exist and are meaningless.
Shouldn't we call the method paint like obj.paint()?
As noted by MightyPork (1+ to him), the paint method is called by the JVM, and should not be directly called by you. You can suggest that it be called by calling repaint(), but you should almost never call it, or the paintComponent(...) method, directly.
Other issues:
When painting in Swing, don't forget to call the super paint method within your own override. Otherwise the component will not do its own house-keeping painting, and you will break the painting chain, meaning components held by the painted container might not be painted correctly or at all.
In general, you will want to not want to draw directly within a JFrame, since a JFrame is responsible for many key components, and if you messed something up, the GUI could be messed up.
Much better would be to draw in a JPanel's paintComponent method, and then display that JPanel within your JFrame.
Check out the tutorials for all the gory but very important details:
Lesson: Performing Custom Painting: introductory tutorial to Swing graphics
Painting in AWT and Swing: advanced tutorial on Swing graphics
Edit You ask:
So, when use API, we just "fill" with code the methods in the class, and the JVM will know when to call these methods?
Most often Swing graphics is "passive". Meaning you set your component up to be drawn a certain way, but you don't actively draw it yourself. Rather the JVM (the Java Virtual Machine -- the code that runs your Java program) does the drawing for you. If you want to do animation, often you'll change some positional variables, for instance xPosition and yPosition int variables, and then use those variables within your JPanel's paintComponent(Graphics g) method. So if you change the values held by these variables within a Swing Timer, and then call repaint() after the values have changed, the JVM will (usually) repaint your component, using the new values.
A more complex example that shows drawing in a JPanel, and shows a very simple animation:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
// draw in a JPanel, not a JFrame
public class SimpleAnimation extends JPanel {
private static final int OVAL_WIDTH = 40;
private static final int TIMER_DELAY = 50;
private static final int PREF_W = 800;
private static final int PREF_H = PREF_W;
private int xPosition = 0;
private int yPosition = 0;
public SimpleAnimation() {
// start my timer here
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
// call the super method so that the JPanel
// can do its own house-keeping graphics first
super.paintComponent(g);
// I do this to so that my graphics are smooth
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.red);
// use xPosition and yPosition to place my oval
g2.fillOval(xPosition, yPosition, OVAL_WIDTH, OVAL_WIDTH);
}
// so our GUI will be big enough to see
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
// class used by the Swing Timer to drive animation
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// change xPosition and yPosition
xPosition++;
yPosition++;
// and call repaint
repaint();
}
}
// a method to be called in our Runnable
private static void createAndShowGui() {
SimpleAnimation mainPanel = new SimpleAnimation();
JFrame frame = new JFrame("SimpleAnimation");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
// this is used to make sure that all Swing code
// is started on the Swing event thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
You create the object new JavaGame() without assigning it to any variable, and that's it.
Swing render and event threads kick in and take care of the rest of the app's life.
I'm not sure why the Garbage Collector doesn't pick it up and end it, but I've done it this way for a long time and it works fine.
You don't need an object name. When you use new, the constructor will be called, which creates the frame.
Paint gets called whenever it needs to be. Check out this answer.

Invisible objects because repaint method - Java Swing

The problem comes because I overwrite the paintComponent method of a jPanel, so when I repaint all the objets are hidden till I focus them.
I need to overwrite the paintComponent method cause it's the only one answer I'd found in internet to change the background image of a jFrame.
So firstly I create a jPanel class:
public class JPanelFondoPrincipal extends javax.swing.JPanel {
public JPanelFondoPrincipal(){
this.setSize(800,500);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Dimension tamanio = getSize();
ImageIcon imagenFondo = new ImageIcon(getClass().getResource("/images/fondo_principal.png"));
g.drawImage(imagenFondo.getImage(),0,0,tamanio.width, tamanio.height, null);
setOpaque(false);
}
}
And in my jPanelForm:
private void formWindowOpened(java.awt.event.WindowEvent evt) {
// TODO add your handling code here:
JPanelFondo p = new JPanelFondo();
this.add(p);
validate();
p.repaint();
}
I'd already tried to add all my Objets (labels, textFields...) to a new Panel so I can add it after repaint, and set all the objets visibles manually but everything is still invisible.
Many thanks, I need to finish the app in 6 days and I'm getting crazy by the minute
EDIT: SOLVED THANKS TO CARDLAYOUT
don't add / remove JPanels or its contents on runtime, use CardLayout instead
your JPanelFondo p = new JPanelFondo(); doesn't corresponding somehow with public class JPanelFondoPrincipal extends javax.swing.JPanel {
for better help sooner edit your question with an SSCCE,
Swing programs should override paintComponent() instead of overriding paint().
http://java.sun.com/products/jfc/tsc/articles/painting/
And you should call super.paintComponent(g); first in overriden paintComponent();
public void paintComponent(Graphics g){
super.paintComponent(g);
Dimension tamanio = getSize();
ImageIcon imagenFondo = new ImageIcon(getClass().getResource("/images/fondo_principal.png"));
g.drawImage(imagenFondo.getImage(),0,0,tamanio.width, tamanio.height, null);
setOpaque(false);
}
Here's the proper way to handle painting onto JPanel component.

Drawing graphics in java - NetBeans IDE

I created a new JApplet form in NetBeans:
public class UI extends javax.swing.JApplet {
//generated code...
}
And a JPanel in design mode named panou:
// Variables declaration - do not modify
private javax.swing.JPanel panou;
How do I get to draw a line on panou? I've been searching for this for 5 hours now so a code snippet and where to place it would be great. Using Graphics2D preferably.
Go to design mode
Right Click on the panel "panou"
Click "Costumize code"
In the dialog select in the first combobox "costum creation"
add after = new javax.swing.JPanel() this, so you see this:
 
panou = new javax.swing.JPanel(){
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g); // Do the original draw
g.drawLine(10, 10, 60, 60); // Write here your coordinates
}
};
Make sure you import java.awt.Graphics.
The line that you will see is always one pixel thick. You can make it more "line" by doing the following:
Create this method:
public static final void setAntiAliasing(Graphics g, boolean yesno)
{
Object obj = yesno ? RenderingHints.VALUE_ANTIALIAS_ON
: RenderingHints.VALUE_ANTIALIAS_OFF;
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, obj);
}
And add after super.paintComponent(g); (in your costum creation) this:
setAntiAlias(g, true);
Edit:
What you are doing wrong is: you paint the line once (by creating the frame).
When you paint the line the frame is also invisible. The first draw is happening when the frame becomes visible. The frame will be Repainted, so everything from the previous paint will disappear.
Always you resize the frame, everything will be repainted. So you have to make sure each time the panel is painted, the line also is painted.
To do custom painting in a JPanel, one would need to make a subclass of a JPanel, and then overload the paintComponent method:
class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
// Perform custom painting here.
}
}
In the example above, the MyPanel class is a subclass of JPanel, which will perform whatever custom painting is written in the paintComponent method.
For more information on how to do custom painting in Swing components, Lesson: Performing Custom Painting from The Java Tutorials have some examples.
If one wants to do painting with Java2D (i.e. using Graphics2D) then one could do some painting on a BufferedImage first, then draw the contents of the BufferedImage onto the JPanel:
class MyPanel extends JPanel {
BufferedImage image;
public MyPanel() {
Graphics2D g = image.createGraphics();
// Do Java2D painting onto the BufferedImage.
}
public void paintComponent(Graphics g) {
// Draw the contents of the BufferedImage onto the panel.
g.drawImage(image, 0, 0, null);
}
}
Further reading:
Painting in AWT and Swing
Trail: 2D Graphics

Categories

Resources