我目前正在开发我的第一个项目,一个使用Java Swing和AWT的简单画图应用程序.在实现绘制功能时,我遇到了准确捕获鼠标移动的问题,特别是在快速移动鼠标时.

我已经设计了应用程序来更新绘图坐标以响应鼠标事件(PaintPanel类中的MouseDraged和MouseMoved方法),从而触发重绘来呈现绘图.然而,尽管我做出了努力,我还是注意到,快速的鼠标移动有时会导致跳过点,导致绘制的线条出现间隙.

下面是我的PaintPanel类,它管理绘画功能:

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();
    }
}

下面是UtilePanel类(如果它有帮助):

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);
            }
        }
}

此外,我try 加入一个游戏循环来持续轮询鼠标输入,希望它能提高捕捉鼠标移动的准确性.然而,即使在游戏循环到位的情况下,问题仍然存在.

我不确定我的绘画方法是否正确,或者是否有更好的方法来这样做.

有没有人能就如何改进鼠标事件捕获以确保精确呈现,特别是在快速鼠标移动期间提供见解或建议?

您的帮助我们将不胜感激.谢谢!

推荐答案

您将希望:

  1. 首先创建BufferedImage,然后创建任何绘制方法的outside.绘制方法应该是精简和快速的,而不是持有可能会耗费时间或内存的代码,因为不必要地减慢速度会对代码的响应性产生很大影响.
  2. 绘制连接当前点和前一点的线,而不是点.
  3. Always在重写中调用super.paintComponent(g)方法.

例如:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;

@SuppressWarnings("serial")
public class PaintPanel02 extends JPanel {
    private static final float BRUSH_SIZE = 10f;
    private static final Stroke DRAWING_STROKE = new BasicStroke(BRUSH_SIZE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
    private static final int PREF_W = 800;
    private static final int PREF_H = 550;
    private BufferedImage image;
    private Color brushColor = null;
    private JButton clearButton = new JButton("Clear");

    public PaintPanel02() {
        setBackground(Color.white);
        MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
        addMouseListener(myMouseAdapter);
        addMouseMotionListener(myMouseAdapter);
        image = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);

        clearButton.addActionListener(e -> {
            image = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
            repaint();
        });
        add(clearButton);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(PREF_W, PREF_H);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2D = (Graphics2D) g;
        g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        if (image != null) {
            g2D.drawImage(image, 0, 0, this);
        }
    }

    private class MyMouseAdapter extends MouseAdapter {
        private Point p0 = null;
        private Point p1 = null;

        public void mousePressed(MouseEvent e) {
            p0 = e.getPoint();

            // let's create a random color for each curve that we draw
            float hue = (float) Math.random();
            float sat = (float) Math.random() / 2f + 0.5f;
            float bright = (float) Math.random() / 2f + 0.5f;
            brushColor = Color.getHSBColor(hue, sat, bright);
        }

        public void mouseReleased(MouseEvent e) {
            p0 = null;
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            if (p0 == null) {
                // if we're not drawing, get out of here
                return;
            }
            p1 = e.getPoint();
            Graphics2D g2D = image.createGraphics();
            g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2D.setColor(brushColor);
            g2D.setStroke(DRAWING_STROKE);
            g2D.drawLine(p0.x, p0.y, p1.x, p1.y);
            g2D.dispose();
            p0 = p1;
            repaint();
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("PaintPanel02");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new PaintPanel02());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

Java相关问答推荐

为什么Java中的两个日期有差异?

如何在Javascript中设置文本区域圆角的样式

Java中后期绑定的替代概念

Java .类参数不通过构造函数传递

存根基类的受保护方法

Java 21 struct 化连接货币,需要可预知的子任务异常排序

这是什么Java构造`(InputStream Is)->;()->;{}`

Javadoc在方法摘要中省略方法

用OSQL创建索引

我的Spring Boot测试显示&IlLegalStateException:无法加载某事的ApplicationContext.

为什么同步数据块无效?

JavaFX标签中的奇怪字符

循环不起作用只有第一个元素重复

使用MediaPlayer类在一段时间后停止播放音乐

具有多个分析模式的复杂分隔字符串的正则表达式

如何使用外部函数从Java中获取C++ struct 的返回值&;内存API

读取ConcurrentHashMap中的可变对象

javax.crypto-密码对象-提供者服务是如何工作的?

在JSON上获取反斜杠

人们在 IntelliJ 上将-Dhttp.proxyHost=your.proxy.net -Dhttp.proxyPort=8080放在哪里?