The following code is supposed to print
inside init()-- inside start()-- inside paint().
But it prints the last part inside paint() TWICE! Why is that?
public class SampleApplet extends Applet {
String msg;
#Override
public void init(){
setBackground(Color.BLACK);
setForeground(Color.yellow);
msg = "Inside init()-- ";
}
#Override
public void start(){
msg += "Inside start()-- ";
}
#Override
public void paint(Graphics g){
msg += "Inside paint().";
g.drawString(msg, 10, 30);
}
}
Quoted from: Paint():
the paint() method will be called as many times as necessary. If you
put another window over your GUI then the paint() method will be
called. If you then minimize that window and make your GUI visible
again then the paint() method will be called again. And so on.
So if you have something that is a problem if the paint() method is
called more than once, you have done it wrong. Don't do it that way.
The paint() method should only redraw its target from existing data,
it should never have to do calculations to figure out what to paint.
The paint method may be called by update when the component needs to repaint the content of the component state is invalidated.
Related
I am creating a java program and want to pause the program until a mouse click using a MouseListener is identified. How would I 'pause' the program, for example using a loop, in such a way that the MouseListener still works and the program can return to the same method?
I have tried putting a loop to stop the program until a variable is true, but the MouseListener cannot work if a loop is running.
I have also attempted to put the rest of my code in the mouseClicked method, or running new methods from within mouseClicked, however another error occurred as I cannot use Graphics g anywhere except for paintComponent, for this reason it seems to me that a loop is necessary to pause the program.
Here is a simplified program which I created to show the core of the problem. (Not full code).
class Surface extends JPanel implements MouseListener{
#Override
public void mouseClicked(MouseEvent arg0) {
//Some way to unpause program
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawLine(30, 30, 200, 30);
//The program needs to 'pause' here until a click is identified.
System.out.println("Program finishes here");
}
}
The MouseListener does work, however it only seems to work if the program is dormant, and has finished all of the code in paintComponent.
Here is the code that does not work, as a loop is running.
class Surface extends JPanel implements MouseListener{
public static boolean repeatLoop = true;
#Override
public void mouseClicked(MouseEvent arg0) {
repeatLoop = false;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawLine(30, 30, 200, 30);
while (repeatLoop) {
}
System.out.println("Program finishes here");
}
}
I expected a loop telling the thread to sleep might work as well, but this has the same outcome and the MouseListener cannot be called.
Therefore I am asking why can a MouseListener not function when a loop is running, and is there an easy way to avert this problem and pause the program until the mouseClicked code is run.
Your loop in the paintComponent blocks the main thread, this is why this won't work. You shouldn't put that kind of logic in the paintComponent. The best thing you could do is create a separate Thread which checks for the repeatLoop. If the reapetLoop variable gets to false you could finish the application.
The paintComponent method is there just to paint on the JPanel.
Why doesn't this work? I created a method for an animation loop of two pictures called animation(),
and i want to put that in the paint method. How do i do this?
import java.applet.*;
import java.awt.*;
public class Slot_Machine extends Applet
{
Image back;
int coin=10;
// Place instance variables here
public void init ()
{
back= getImage(getDocumentBase(),"Images/Background/Slot Machine.png");
// Place the body of the initialization method here
} // init method
public void animation(Graphics g)
{
Image Title1,Title2;
Title1= getImage(getDocumentBase(),"Images/Title Animation/Title 1.png");
Title2= getImage(getDocumentBase(),"Images/Title Animation/Title 2.png");
while(true){
g.drawImage(Title2,200,0,this);
{ try { Thread.currentThread().sleep(2000); }
catch ( Exception e ) { } }
g.drawImage(Title1,200,0,this);
{ try { Thread.currentThread().sleep(2000); }
catch ( Exception e ) { } }
}//end while(true) loop
}//end animation
public void paint (Graphics g)
{
g.drawImage(back,0,0,this);
animation(); //FROM THE METHOD ABOVE, WHY DOESNT THIS WORK? HOW DO I FIX THIS?
String coins = String.valueOf(coin);
g.setColor(Color.white);
g.setFont(new Font("Impact",Font.PLAIN,30));
g.drawString(coins,405,350);
// Place the body of the drawing method here
} // paint method
}
Your animation() method has an infinite loop in it, which means that paint() never returns, which is causing the thread trying to start up the applet to hang while waiting for paint() to return.
If you want to run a forever-looping animation in the background, try doing it in another thread. Take a look at ExecutorService.
I'm new for AWT/Swing programming. I try to draw an image 100,000 times. The loop of drawImage() method is in paint() method. I find the times that java calls paint() method is quite unstable. It may call paint() method two times in one running time of program, but others may call three times. If I create a button that does repaint(), it would only call paint() one time each I click this button.
Could someone tell me when the paint() method would be called? I know System-triggered Painting and App-triggered Painting. But I think it doesn't belong to any of these cases.
You can use another thread to call the repaint() metho. An easy way to do this is to make your painting class (java.awt.Canvas or as here javax.swing.JPanel) implement Runnable and start that Thread in the constructor. Also add the run() method.
import javax.swing.*;
public class DrawPicktures extends JPanel implements Runnable
{
public DrawPickture()
{
/* Code */
new Thread(this).start();
}
public void Paint(Graphics g)
{
super.Paint(g); // Will draw all gui components added
/* Code */
}
public void run()
{
while(true)
{
repaint();
try
{
Thread.sleep(1000 /* Waits for 1000 milliseconds */);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
}
Because the JPanel (or Canvas) will be repainted from another thread it won't block up your main thread so your program can do other stuff at the same time.
im wondering how a jcomponent gets painted on the screen, is it painted inside paintComponent() from Graphics? Or is it painted separately. Im asking this because its weird how a jbutton changes color on mousehover even though repaint() is never called.
Thanks for your time.
Components are painted with their paint method. repaint is just a useful method that will call paint at some point in the near future on the Event Dispatch Thread.
When the mouse enters a JButton, the following method is called (for JButtons with a default UI):
public void mouseEntered(MouseEvent e) {
AbstractButton b = (AbstractButton) e.getSource();
ButtonModel model = b.getModel();
if (b.isRolloverEnabled() && !SwingUtilities.isLeftMouseButton(e)) {
model.setRollover(true);
}
if (model.isPressed())
model.setArmed(true);
}
ButtonModel.setRollover will fire a ChangeEvent, which is handled by AbstractButton in the following way:
public void stateChanged(ChangeEvent e) {
Object source = e.getSource();
updateMnemonicProperties();
if (isEnabled() != model.isEnabled()) {
setEnabled(model.isEnabled());
}
fireStateChanged();
repaint();
}
So repaint is called when the mouse enters a JButton.
..a jbutton changes color on mousehover even though repaint() is never called.
Sure it is. And this code is evidence of that. Of course, it is not evidence on a Kindle Fire that most probably has no JRE, but then, a Kindle Fire is an entirely inappropriate tool to be using to communicate to a Q&A site while discussing technical points of a programming language that does not run on the device.
import javax.swing.*;
public class ButtonRepaint {
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JButton b = new JButton("Hover Over Me!") {
#Override
public void repaint() {
super.repaint();
System.out.println("Repaint");
}
};
JOptionPane.showMessageDialog(null, b);
}
};
SwingUtilities.invokeLater(r);
}
}
Note that the paint() method that gets called belongs to the button's UI delegate, typically derived from BasicButtonUI. There's an example here using MetalButtonUI.
This is a part of my code which does some sort of animation; however, there seems to be something wrong: Whenever I try to use the passed 'g' from inside the anonymous class to draw anything, it does nothing, yet when I used it outside the anonymous class (inside the rollBalls method) it does what it's supposed to do. Any idea why? And how do I fix this? Thank you.
protected void paintComponent(Graphics g) {
super.paintComponent(g);
rollBalls(g);
}
private void rollBalls(final Graphics g) { //Roll all 3 balls on the bar
new Timer(1, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
g.setColor(Color.red);
g.fillRect(0, 0, 500, 500);
}
}).start();
}
Your problem is several fold but first and foremost you understand that the Graphics object passed into a paint or paintComponent method usually does not persist and may be disposed of after the method completes. Next you have program logic being called from within a paintComponent method which should never be done. You do not have full control of when or even if the paint or paintComponent methods are called and thus should not have it dictating your app's logic.
For this reason you do not do Swing graphics in this way. Instead, have your timer outside of any paintComponent method, have it update class fields, have it then call repaint() and have your paintComponent use these class fields to do drawing as needed and this time with a stable Graphics object that is passed into it via its parameters.
I understand that your code is "just a sample", but your doing things wrong by trying to paint directly from within actionPerformed. You simply shouldn't do this.
I agree with Hovercraft Full Of Eels's comment. you should do something like this
class MyClass extends JComponent{
MyClass(){
new Timer(1, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
MyClass.this.repaint();
}
}).start();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(0, 0, 500, 500);
}
Maybe living on the wrong thread for GUI update? Try putting the drawing commands in the anonymous class into a Runnable and pass that to SwingUtilities.invokeLater.
I don't think it has anything to do with inner-class-ness.