代码之家  ›  专栏  ›  技术社区  ›  Oussama Mahjour

在Java Swing paint应用程序中捕捉鼠标快速移动的困难

  •  0
  • Oussama Mahjour  · 技术社区  · 1 年前

    我目前正在开发我的第一个项目,一个使用JavaSwing和AWT的简单绘画应用程序。在实现绘画功能时,我遇到了准确捕捉鼠标移动的问题,尤其是在快速移动鼠标时。

    我已经设计了这个应用程序来更新绘图坐标以响应鼠标事件(PaintPanel类中的mouseDragged和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);
                }
            }
    }
    

    此外,我试图加入一个游戏循环来持续轮询鼠标输入,希望它能提高鼠标移动捕捉的准确性。然而,即使有了游戏循环,问题仍然存在。

    我不确定我在paintComponent中省略super.paintComponent(g)的绘画方法是正确的还是有更好的方法。

    有人能就如何改进鼠标事件捕捉以确保精确渲染提供见解或建议吗,尤其是在鼠标快速移动时?

    非常感谢您的协助。非常感谢。

    1 回复  |  直到 1 年前
        1
  •  0
  •   Hovercraft Full Of Eels    1 年前

    您将希望:

    1. 首先创建BufferedImage,然后 外部 任何绘画方法。绘制方法应该是精简和快速的,并且不包含可能在时间或内存方面代价高昂的代码,因为不必要地减慢它会对代码的感知响应产生很大影响。
    2. 绘制连接当前点和先前点(而不是点)的线。
    3. 总是 呼叫 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);
        }
    }