I'm currently developing my first project, a simple paint application using Java Swing and AWT. While implementing the painting functionality, I encountered an issue with accurately capturing mouse movements, especially when moving the mouse quickly.
I've designed the application to update the drawing coordinates in response to mouse events (mouseDragged and mouseMoved methods in the PaintPanel class), triggering repaints to render the drawings. However, despite my efforts, I've noticed that fast mouse movements sometimes result in skipped points, leading to gaps in the drawn lines.
Here's my PaintPanel class, which manages the painting functionality:
public class PaintPanel extends JPanel implements MouseMotionListener{
public Point mouseCoordinates;
boolean painting = false;
public PaintPanel() {
this.setPreferredSize(new Dimension(1000,550));
this.setBackground(Color.white);
this.addMouseMotionListener(this);
}
public void paintComponent(Graphics g) {
Graphics2D g2D = (Graphics2D) g;
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if(painting == false) {
super.paintComponent(g2D);
}
if(mouseCoordinates != null) {
g2D.setColor(UtilePanel.col);
g2D.fillOval((int)mouseCoordinates.getX(),(int)mouseCoordinates.getY(),UtilePanel.brushSize, UtilePanel.brushSize);
this.setCursor( this.getToolkit().createCustomCursor(
new BufferedImage( 1, 1, BufferedImage.TYPE_INT_ARGB ),
new Point(),
null ) );
}
}
@Override
public void mouseDragged(MouseEvent e) {
mouseCoordinates = e.getPoint();
painting = true;
repaint();
}
@Override
public void mouseMoved(MouseEvent e) {
mouseCoordinates = e.getPoint();
repaint();
}
}
Here's the UtilePanel class if it help :
public class UtilePanel extends JPanel {
static Color col=Color.black;
static int brushSize = 5;
public UtilePanel(){
this.setPreferredSize(new Dimension (1000,150));
JPanel container = new JPanel();
container.setLayout(new GridLayout(1,0));
this.setLayout(new GridLayout(0,1));
Brushes brushes = new Brushes();
Shapes shapes = new Shapes();
LoadImage loadImage = new LoadImage();
container.add(brushes);
container.add(shapes);
container.add(loadImage);
this.add(new JPanel());
this.add(container);
this.add(new JPanel());
setBorder(BorderFactory.createEtchedBorder(0));
}
public class Brushes extends JPanel{
JRadioButton Size1;
JRadioButton Size2;
JRadioButton Size3;
JRadioButton Size4;
ButtonGroup group;
JButton color;
public Brushes() {
Size1 = new JRadioButton();
Size2 = new JRadioButton();
Size3 = new JRadioButton();
Size4 = new JRadioButton();
group = new ButtonGroup();
color = new JButton();
color.setBackground(col);
color.setBorder(BorderFactory.createEtchedBorder(0));
color.setPreferredSize(new Dimension(20,20));
Size1.setSelected(true);
JColorChooser colorchooser= new JColorChooser();
color.addActionListener(e->{
col = JColorChooser.showDialog(null, "Pick a color ",Color.black);
color.setBackground(col);
});
Size1.addActionListener(e->{
brushSize = 5;
});
Size2.addActionListener(e->{
brushSize = 10;
});
Size3.addActionListener(e->{
brushSize = 15;
});
Size4.addActionListener(e->{
brushSize = 20;
});
group.add(Size1);
group.add(Size2);
group.add(Size3);
group.add(Size4);
this.add(Size1);
this.add(Size2);
this.add(Size3);
this.add(Size4);
this.add(color);
this.setLayout(new FlowLayout());
}
}
public class Shapes extends JPanel{
public Shapes() {
ShapeButton circule = new ShapeButton ("circule");
ShapeButton rect = new ShapeButton ("Rectangle");
ShapeButton triangle = new ShapeButton ("Tri");
ShapeButton line = new ShapeButton ("Line");
this.setLayout(new FlowLayout());
ButtonGroup bg = new ButtonGroup();
bg.add(rect);
bg.add(triangle);
bg.add(circule);
bg.add(line);
this.add(circule);
this.add(rect);
this.add(triangle);
this.add(line);
}
class ShapeButton extends JRadioButton {
public ShapeButton(String s) {
setIcon((new ImageIcon(creatImage(new Color(0x00FFFFFF, true),s))));
setSelectedIcon(new ImageIcon(creatImage(Color.gray,s)));
}
}
public BufferedImage creatImage(Color color,String shape) {
BufferedImage bi = new BufferedImage(40,40, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) bi.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setStroke(new BasicStroke(5));
g.setColor(color);
g.fillRect(0,0,40,40);
g.setColor(Color.black);
switch(shape) {
case "circule":
g.drawOval(5, 5, 30,30);
break;
case "Rectangle":
g.drawRect(5,5,30,30);
break;
case "Tri":
int[] X= {5,16,30};
int[] Y= {30,5,30};
g.drawPolygon(X,Y,3);
break;
case "Line":
g.drawLine(5,30,30,5);
break;
}
//g.dispose();
return bi;
}
}
public class LoadImage extends JPanel{
public LoadImage() {
JButton loadButton = new JButton("Import Image");
loadButton.setPreferredSize(new Dimension(100,50));
JFileChooser f = new JFileChooser();
loadButton.addActionListener(e->{
int resp = f.showOpenDialog(null);
if(resp == JFileChooser.APPROVE_OPTION) {
File file = new File(f.getSelectedFile().getAbsolutePath());
System.out.println(file.getAbsolutePath());
}
});
this.add(loadButton);
}
}
}
Additionally, I attempted to incorporate a game loop to continuously poll for mouse input, hoping it would improve the accuracy of mouse movement capturing. However, even with the game loop in place, the problem persists.
I'm unsure if my approach to painting by omitting super.paintComponent(g) in paintComponent is the correct way or there's a better way to do it.
Could someone provide insights or suggestions on how to improve the mouse event capturing to guarantee precise rendering, especially during rapid mouse movements?
Your assistance would be greatly appreciated. Thank you!
You will want to:
super.paintComponent(g)method within your override.For example: