Using Graphics from anonymous class - java

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.

Related

Why does a MouseListener not function whilst a loop is running?

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.

Repaint function messes up the whole frame

I have a problem when trying to draw some elements using paint method in Swing.
As title says, my whole frame collapses and does some weird repeating.
I made a separate JPanel so I can manipulate drawn shapes:
public class PanelPovrsina extends JPanel{
private ArrayList<Oblik> listaOblika;
public PanelPovrsina() {
// svi oblici
this.listaOblika = new ArrayList<Oblik>();
this.listaOblika.add(new Kvadrat(new Tacka(50, 50), 50, "zuta", "crvena"));
this.setBackground(Color.WHITE);
this.setVisible(true);
}
public void paint(Graphics g) {
if(this.listaOblika.isEmpty()) return;
Iterator<Oblik> it = this.listaOblika.iterator();
while(it.hasNext()) {
it.next().crtajUBoji(g);
}
repaint(); // this causes problems!
}
public ArrayList<Oblik> getListaOblika() {
return this.listaOblika;
}
}
Here is the frame with this code:
And here it is without repaint method:
No, I know repaint method is essential in order to dynamically add shapes and actually draw, but I can't make this work correctly.
Also, as you can see from the code above, background of panel is set to white, but my frame would'n render it.
Hope there is enough information to solve my problem, if not, I will add code of my JFrame!
Thank you!
You should never override the paint method, as it handles a number of other things behind the scenes. You should override paintComponent instead.
As #Joe C answered, I should have been using paintComponent method, not paint! Working code:
public class PanelPovrsina extends JPanel{
private ArrayList<Oblik> listaOblika;
public PanelPovrsina() {
// svi oblici
this.listaOblika = new ArrayList<Oblik>();
this.listaOblika.add(new Kvadrat(new Tacka(50, 50), 50, "zuta", "crvena"));
this.setBackground(Color.PINK);
this.setVisible(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Oblik obl : this.listaOblika) {
obl.crtajUBoji(g);
}
repaint();
}
public ArrayList<Oblik> getListaOblika() {
return this.listaOblika;
}
}

Java Graphics: setClip vs clipRect vs repaint(int,int,int,int)

Similar to my last post apologies, but a lot less long winded. Basically I am wondering what is the best option for optimising redrawing to a JFrame/JPanel when each repaint call only a small part of the screen would be redrawn.
Also apart from overloading repaint I am not 100% on how to implement setClip or clipRect. My understanding they are used when overriding paint or update?
See below for some code:
public class AquaSim extends JPanel {
//variables
//other methods...
public void paint (Graphics g) {
super.paintComponent(g); //needed?
Graphics2D g2d = (Graphics2D) g;
//Draws the land area for penguins.
g2d.setColor(new Color(121,133,60));
g2d.fill(land);
g2d.setColor(Color.BLUE);
g2d.fill(sea);
drawCreatures(g2d);
}
public void drawCreatures(Graphics2D g2d) {
for (Creature c : crlist) //a list of all alive creatures {
//each creature object stores its image and coords.
g2d.drawImage(c.createImage(txs,tys), c.getPos(1).width, c.getPos(1).height, this);
}
}
}
Ideally I would prefer not to have to loop though each creature object each repaint request, which is part of the reason for this post. I dont know if there would be a way of sending the current creature being drawn to paint or overriding paint in the Creature class to get it draw itself on to the graphics object in the main class.
A bit more code...
private class Creature implements ActionListener {
//variables & other methods
#Override
public void actionPerformed(ActionEvent e) {
if (getState()!=State.DEAD) {
move();
repaint(); //<---Would rather set clipping area in paint/update. x,y,w,h needs to include ICON & INFO BOX.
//repaint(gx,gy,getPos(1).width,getPos(1).height);
}
else anim.stop();
}
//...
public void move() {
//Determines how it will move and sets where to here by updating its position that is used in drawCreatures.
}
}
So any suggestions as which would be the most efficient method to use? Baring in mind repaint will be called a lot by many objects/creatures i.e. many times a second, hence why I dont want it redrawing everythin on the screen each repaint request.
only a small part of the screen would be redrawn.
Use repaint(....).
The RepaintManager will worry about what needs to be painted and will set the clip of the Graphics object for you.
Just out of interest is it possible to make the Graphics object attached to the JPanel in AquaSim global and then from within each Creature object use this graphics object to draw to the JPanel?
Although I guess I would need to figure out how to implement/override the paint method within the Creature class a bit like Gilbert is trying to tell me.

Button not working when trying to draw a line

I'm new to Java and teaching myself and trying to build this program. It will create dots when the mouse clicks, but won't draw a line when the button is pushed. (eventually the line will connect the dots, but I'm not there yet.) First I just need to get it to draw something when pushed and work from there. I've tried multiple things and I can't get it to work. Here is my code:
public void init()
{
LineDrawListener listener = new LineDrawListener ();
addMouseListener (listener);
Button lineGraph = new Button("Graph Line");
lineGraph.addActionListener (this);
add (lineGraph, BorderLayout.EAST);
setBackground (Color.yellow);
setSize (APPLET_WIDTH, APPLET_HEIGHT);
}
public void paint(Graphics g)
{
// draws dots on screen
g.setColor(Color.blue);
if (current != null)
{
xcoordinates.addElement (current.x);
ycoordinates.addElement (current.y);
count++;
g.fillOval (current.x-4, current.y-4, 10, 10);
repaint();
}
if (push == true)
{
g.drawLine (5, 5, 30 , 30);
repaint();
}
}
class LineDrawListener extends MouseAdapter
{
public void mouseClicked (MouseEvent event)
{
current = event.getPoint();
repaint();
}
}
public void actionPerformed (ActionEvent event)
{
Object action = event.getSource();
if(action==lineGraph)
{
push = true;
}
}
Any help on how to get the button to work would be much appreciated. Thanks in advance.
The reason you can not get the line to draw is that repaint() posts a request to repaint the component. You are using it as if it refreshes the view somehow. You need to change three things in your code, all in paint():
Do not call repaint() in paint()
Override paintComponent() rather than paint() so your borders get drawn if you should ever have them at a later time.
Call super.paintComponent() in paintComponent() to do some initialization for you. It is probably OK not to for something this simple, but good practice for the future.
Here is what the code would look like:
public void paintComponent(Graphics g)
{
super.paintComponent(g);
// draws dots on screen
g.setColor(Color.blue);
if (current != null)
{
xcoordinates.addElement (current.x);
ycoordinates.addElement (current.y);
count++;
g.fillOval (current.x-4, current.y-4, 10, 10);
}
if (push == true)
{
g.drawLine (5, 5, 30 , 30);
}
}
Don't paint on top-level containers like JApplet
Rather paint on a JPanel and override the paintComponent method and call super.paintComponet(g)
#Override
protected void paintComponent(Graphic g){
super.paintComponent(g);
...
}
Don't call repaint() from inside the paint() method. Call it in your actionPerformed()
Learn to post an SSCCE
Number 3 is your most dire problem
Don't override paint(). Don't invoke repaint() in a painting method, this can cause in infinite loop.
Check out Custom Painting Approaches for working examples of the two common ways to do custom paintint:
by using a List to track the objects to be painted
by painting to a BufferedImage

How do I get paintComponent(Graphics g) to call? repaint() won't work every time

The following in a Java SE6 program that is a tic tac toe game:
I have a class MiniPanel that extends JPanel that I have in a grid. If the user clicks one of these, a listener does some stuff then... eventually, it calls a method placeX() or placeO() on the MiniPanel that was clicked. That does some stuff then prints out a message saying that the method was called, and the very next line calls an animatePanel() method, which calls repaint() inside, printing out a message right before.
The class MiniPanel also overrides paintComponent(Graphics g) with a method that first prints out a message saying that paintComponent was called THEN does super.paintComponent(g) and then does other stuff.
The problem is that when I click a MiniPanel, the first 2 messages saying that placeX() or placeO() and animatePanel() was called appears, which means that repaint() must have been called. But paintComponent(Graphics g) is never called! Another weird thing is that it works if I call placeX() directly when the MiniPanel is initialized but not otherwise.
I only have paintComponent(Graphics g) overridden, nothing else.
Here is paintComponent:
public void paintComponent(Graphics g){
if (Controller.DEBUG) System.out.println("MiniPanel at ("+row+", "+col+") received paintComponent (is repainting now).");
super.paintComponent(g);
...
...
}
placeX (placeO is pretty much the same thing):
public void placeX(){
...
if (Controller.DEBUG) System.out.println("MiniPanel at ("+row+", "+col+") received placeX.");
...
animatePanel();
}
animatePanel:
private void animatePanel() {
if (Controller.DEBUG) System.out.println("MiniPanel at ("+row+", "+col+") doing animatePanel().");
timer.start();
}
The timer calls this repeatedly for a little animation:
private ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (Controller.DEBUG) System.out.println("MiniPanel at ("+row+", "+col+") doing actionPerformed().");
...
if (Controller.DEBUG) System.out.println("MiniPanel at ("+row+", "+col+") about to repaint... Should receive paintComponent command!");
repaint();
}
};

Categories

Resources