0

so I have a question, lets say I create a rectangle in Java using the paint method, after a 100 ms delay I do super.paint(g), this clears the rectangle previously shown, is there a way to make it re appear?

Thanks!

An example of what I'm talking about is down below, what this program is meant to do is whenever I hold down mouse button 1, it creates a rectangle that goes down and than disapears after mouse button 1 is off. The problem is whenever I hold down mouse button 1 again, the rectangle doesn't appear.

First class:

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;

import javax.swing.JFrame;
import javax.swing.Timer;

public class RecoilHelper extends JFrame {


static Timer rs;
static int recoil = 540;
static boolean clearRectangle = false;

/**
 * Launch the application.
 */
public static void main(String[] args) {
    JNativehookRecoilHelp.main(null);
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                RecoilHelper frame = new RecoilHelper();
                frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

/**
 * Create the application.
 */
public RecoilHelper() {
    initialize();
}

/**
 * Initialize the contents of the frame.
 */
private void initialize() {
    
    setBounds(0, 0, 1920, 1080);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setUndecorated(true);
    setBackground(new Color(1.0f,1.0f,1.0f,0.0f));
    setAlwaysOnTop(true);
    
    rs = new Timer(10,(ActionEvent e)->{
        repaint();
        
        recoil += 12;
    
    
        if (recoil>600) {
            
            rs.stop();
        }
        
    });

    
}

public void paint(Graphics g) { 
     
    Rectangle r = new Rectangle(960, recoil, 4, 4);
    System.out.println(recoil);
    super.paintComponents(g);
    g.fillRect(
       (int)r.getX(),
       (int)r.getY(),
       (int)r.getWidth(),
       (int)r.getHeight()
    );  
    if (clearRectangle) {
        super.paint(g);
    } 
    
}

}

Second class(tracks mouse button 1 events using JNativehook):

import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.jnativehook.mouse.NativeMouseEvent;
import org.jnativehook.mouse.NativeMouseInputListener;

public class JNativehookRecoilHelp implements NativeMouseInputListener {

@Override
public void nativeMouseClicked(NativeMouseEvent arg0) {
    // TODO Auto-generated method stub

}

@Override
public void nativeMousePressed(NativeMouseEvent arg0) {
    // TODO Auto-generated method stub
    System.out.println("Pressed");
    RecoilHelper.recoil = 540;
    RecoilHelper.rs.start();

}

@Override
public void nativeMouseReleased(NativeMouseEvent arg0) {
    // TODO Auto-generated method stub
    System.out.println("Released");
    RecoilHelper.clearRectangle=true;
    
}

@Override
public void nativeMouseDragged(NativeMouseEvent arg0) {
    // TODO Auto-generated method stub
    
}

@Override
public void nativeMouseMoved(NativeMouseEvent arg0) {
    // TODO Auto-generated method stub
    
}
public static void main(String[] args) {
    GlobalScreen.addNativeMouseListener(new JNativehookRecoilHelp());
    LogManager.getLogManager().reset();

    // Get the logger for "org.jnativehook" and set the level to off.
    Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName());
    logger.setLevel(Level.OFF);

    try {
        GlobalScreen.registerNativeHook();
    }
    catch (NativeHookException ex) {
        


        System.exit(1);
    }
}

}
  • You need to override the `paintComponent` method of the component. I strongly recommend completing this entire tutorial to learn how it works: https://docs.oracle.com/javase/tutorial/uiswing/painting/index.html – sorifiend Apr 23 '21 at 03:14
  • Does this answer your question? [Creating a draw rectangle (filled with black color) function in Java for a grid](https://stackoverflow.com/questions/15870608/creating-a-draw-rectangle-filled-with-black-color-function-in-java-for-a-grid) – sorifiend Apr 23 '21 at 03:16
  • Hi @sorifiend, I'm not entirely sure if this is the answer to my question because when I tried to do something similar, it looks like it doesn't work (or I just did it completely wrong) I had updated my post about this, can you look over it? thanks! – Kayden Boyce Apr 23 '21 at 03:36
  • You are required to call `super.paint` unless you're willing to take over all the duties it performs. Instead, maybe try calling it first then paint your rectangle when `clearRectangle` is `false`. Painting can occur for many different reasons, many which you don't control – MadProgrammer Apr 23 '21 at 03:44

1 Answers1

0

Currently you are attempting to override the paint method of the JFrame, this presents two issues, this first is that a JFrame is a heavyweight component (it has a title bar and a number of associated things that you need to consider) so you may have endless issues, the second issue is that you need to override the paintComponent method of the component that you wish to perform custom painting on.

The solution here is to instead place a JPanel inside the JFrame, and override the paintComponent method of the JPanel.

Below is a working example that creates a new rectangle and adds it to the frame every half second but also keeps the existing rectangles by adding them to a list and drawing each one in the list every time it is repainted.

The main class is simple, and simply adds our CustomJpanel to the JFrame:

public class PaintExample extends JFrame
{
    private CustomJPanel customJpanel;
    
    public PaintExample()
    {
        //Create and add the custom panel to the JFrame
        setPreferredSize(new Dimension(400, 300));
        customJpanel = new CustomJPanel();
        getContentPane().add(customJpanel, java.awt.BorderLayout.CENTER);
        pack();
    }
    
    public static void main(String args[])
    {
        //Show the JFrame:
        java.awt.EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                new PaintExample().setVisible(true);
            }
        });
    }
}

And the custom panel class that does our painting:

public class CustomJPanel extends JPanel
{
    int x = 0;
    int y = 0;
    boolean clearRectangle = true;
    //This is a list that we use to keep track of all the rectangles to draw/update
    ArrayList<Rectangle> rectangleList = new ArrayList<>();
    
    //Start a timer when the panel is created that will update the rectangle location every half second (500ms).
    //In your case you would use your recoil timer instead
    public CustomJPanel(){
        //Create event action
        ActionListener taskPerformer = new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                add a new rectangle in a different location
                x+= 5;
                y+= 5;
                rectangleList.add(new Rectangle(x,y,10,10));
                //Update the panel
                repaint();
            }
        };
        //Create and start a repeating event
        Timer timer = new Timer(500, taskPerformer);
        timer.setRepeats(true);
        timer.start();
    }
    
    //Here is where it all happens:
    @Override
    protected void paintComponent(Graphics g)
    {
        //Call super first to perform normal component painting
        super.paintComponent(g);
        //Now do your custom painting
        if (clearRectangle)
        {
            //Draw each rectangle in the list
            for (Iterator<Rectangle> iterator = rectangleList.iterator(); iterator.hasNext();)
            {
                Rectangle r = iterator.next();
                g.drawRect(r.x, r.y, r.width, r.height);
            }
        }
    }
}

And the window looks like this after a couple so seconds, note how it keeps all previous rectangles: Example output

sorifiend
  • 5,927
  • 1
  • 28
  • 45