Graphics in repaint draws random lines

2019-02-28 06:28发布

问题:

So I'm creating a free hand drawing JPanel, that reacts to mouse movements and draws lines. I got it mostly working except for a bug where it'll randomly draw a straight line between lines. That random straight line isn't intentional, what's drawn on the buffered image is supposed to be strictly what the user draws. These random drawn lines are not done by the user and it's confusing. Below is my code, can anyone take a look? The image included gives you a visual representation of what it is doing.

public class NoteDocument extends JPanel implements MouseListener, MouseMotionListener {

private Frame commands;
private JDesktopPane desktop;
private JInternalFrame colorFrame;
private JPanel colorPanel;
private JColorChooser colorChooser;

private enum State { IDLING, DRAGGING };
private enum ButtonPosition { PRESSED, RELEASED };
private enum Shape { SQUARE, RECTANGLE, CIRCLE, OVAL, LINE };

private State state = State.IDLING;
private ButtonPosition position = ButtonPosition.RELEASED;
private Shape shape = null;

//private ArrayList<Point> points = new ArrayList<Point>();
private ArrayList<Point> pressedPoints = new ArrayList<Point>();
private ArrayList<Point> draggedPoints = new ArrayList<Point>();
private ArrayList<Point> releasedPoints = new ArrayList<Point>();

private BufferedImage bufferedImage = null;

public NoteDocument(Frame commands) {
    this.commands = commands;

    setBackground(Color.WHITE);
    addMouseListener(this);
    addMouseMotionListener(this);

    createColorChooser();
}

private void createColorChooser() {
    for (int i = 0; i < commands.getLayeredPane().getComponentCount(); i++) {
        if (commands.getLayeredPane().getComponent(i) instanceof JDesktopPane) {
            desktop = (JDesktopPane) commands.getLayeredPane().getComponent(i);
        }
    }

    colorChooser = new JColorChooser();
    colorPanel = new JPanel();
    colorFrame = new JInternalFrame();

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(colorPanel);
    colorPanel.setLayout(layout);
    layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addComponent(colorChooser, javax.swing.GroupLayout.PREFERRED_SIZE, 434, javax.swing.GroupLayout.PREFERRED_SIZE)
    );
    layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addComponent(colorChooser, javax.swing.GroupLayout.PREFERRED_SIZE, 328, javax.swing.GroupLayout.PREFERRED_SIZE)
    );

    colorFrame.add(colorPanel);
    desktop.add(colorFrame);

    colorFrame.pack();
    colorFrame.setVisible(true);
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);

     Graphics2D g2 = (Graphics2D) g;
     g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                          RenderingHints.VALUE_ANTIALIAS_ON);

      if(bufferedImage == null)
      {
          int panelHeight = this.getHeight();
          int panelWidth = this.getWidth();
          bufferedImage = (BufferedImage) this.createImage(panelHeight, panelWidth);
          Graphics2D gc = bufferedImage.createGraphics(); 
          gc.setColor(Color.WHITE);   
          gc.fillRect(0, 0, panelWidth, panelHeight);
          g2.drawImage(bufferedImage, null, 0, 0);
      }

      //draw pressed points
      for (int a = 0; a < pressedPoints.size(); a++) {        
          Point p1 = pressedPoints.get(a);
          g.drawLine(p1.x, p1.y, p1.x, p1.y);
      }

      //draw draggedPoints        
      for (int b = 0; b < draggedPoints.size() - 2; b++) {
          Point p1 = draggedPoints.get(b);
          Point p2 = draggedPoints.get(b + 1);

          g.drawLine(p1.x, p1.y, p2.x, p2.y);
      }

      //draw released points
      for (int c = 0; c < releasedPoints.size(); c++) {       
          Point p1 = releasedPoints.get(c);

          g.drawLine(p1.x, p1.y, p1.x, p1.y);
      }
}

@Override
public void mouseClicked(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}

@Override
public void mousePressed(MouseEvent e) {
    if (e.getButton() == MouseEvent.BUTTON1) {
        position = ButtonPosition.PRESSED;
        state = State.DRAGGING;
        pressedPoints.add(e.getPoint());
        this.repaint();     
    } else if (e.getButton() == MouseEvent.BUTTON2) {
        //TODO right click popup
    }
}

@Override
public void mouseReleased(MouseEvent e) {   
    if (state == State.DRAGGING) {
        releasedPoints.add(e.getPoint());
        position = ButtonPosition.RELEASED;
        state = State.IDLING;
        this.repaint();
    }
}

@Override
public void mouseDragged(MouseEvent e) {
    if ((state == State.DRAGGING) && (position == ButtonPosition.PRESSED)) {
        draggedPoints.add(e.getPoint());
        this.repaint();
    } else if ((state == State.IDLING) && (position == ButtonPosition.RELEASED)) {
        return;
    }
}

@Override
public void mouseMoved(MouseEvent e) {
    if ((state == State.DRAGGING) && (position == ButtonPosition.PRESSED)) {
        draggedPoints.add(e.getPoint());
        this.repaint();
    } else if ((state == State.IDLING) && (position == ButtonPosition.RELEASED)) {
        return;
    }
}
}

回答1:

You will likely want to nest Lists to achieve your goal:

List<List<Point>>

This way, when the mouse is pressed, start an ArrayList, and when released, finish it. Then you can use nested for loops to draw all the curves.

For example (from a previous answer of mine):

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;

public class LineDrawEg {

   private static void createAndShowGui() {
      JPanel mainPanel = new JPanel(new GridLayout(1, 0));
      mainPanel.setPreferredSize(new Dimension(800, 400));

      MyMouseAdapter mouseAdapter = new MyMouseAdapter();
      JPanel[] panels = {new Panel1(), new Panel2()};
      for (int i = 0; i < panels.length; i++) {
         String title = "Panel " + (i + 1);
         Border border = new TitledBorder(title);
         panels[i].setBorder(border);
         panels[i].addMouseListener(mouseAdapter);
         panels[i].addMouseMotionListener(mouseAdapter);
         mainPanel.add(panels[i]);
      }

      JFrame frame = new JFrame("Line Draw Eg");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class Panel1 extends JPanel implements Positionable {
   private int xPos = 0;
   private int yPos = 0;

   @Override
   protected void paintComponent(Graphics g) {
      // super.paintComponent(g);
      g.setColor(Color.red);
      g.fillOval(xPos, yPos, 5, 5);
   }

   @Override
   public void mouseDragged(Point p) {
      xPos = p.x;
      yPos = p.y;
      repaint();
   }

   @Override
   public void mousePressed(Point p) {
      xPos = p.x;
      yPos = p.y;
      repaint();
   }

   @Override
   public void mouseReleased(Point p) {
      xPos = p.x;
      yPos = p.y;
      repaint();
   }

}

class Panel2 extends JPanel implements Positionable {
   private List<List<Point>> listOfLists = new ArrayList<List<Point>>();
   private List<Point> currentPoints;

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2 = (Graphics2D) g;
      g2.setStroke(new BasicStroke(5f));
      if (currentPoints != null && currentPoints.size() > 1) {
         g2.setColor(Color.blue);
         for (int i = 1; i < currentPoints.size(); i++) {
            int x1 = currentPoints.get(i - 1).x;
            int y1 = currentPoints.get(i - 1).y;
            int x2 = currentPoints.get(i).x;
            int y2 = currentPoints.get(i).y;
            g2.drawLine(x1, y1, x2, y2);
         }
      }
      g2.setColor(Color.red);
      for (List<Point> pointList : listOfLists) {
         if (pointList.size() > 1) {
            for (int i = 1; i < pointList.size(); i++) {
               int x1 = pointList.get(i - 1).x;
               int y1 = pointList.get(i - 1).y;
               int x2 = pointList.get(i).x;
               int y2 = pointList.get(i).y;
               g2.drawLine(x1, y1, x2, y2);
            }
         }
      }
   }

   @Override
   public void mousePressed(Point p) {
      currentPoints = new ArrayList<Point>();
      currentPoints.add(p);
      repaint();
   }

   @Override
   public void mouseDragged(Point p) {
      currentPoints.add(p);
      repaint();
   }

   @Override
   public void mouseReleased(Point p) {
      if (currentPoints != null) {
         currentPoints.add(p);
         listOfLists.add(currentPoints);
      }
      currentPoints = null;
      repaint();
   }

}

class MyMouseAdapter extends MouseAdapter {
   @Override
   public void mouseDragged(MouseEvent mEvt) {
      Positionable positionable = (Positionable) mEvt.getSource();
      positionable.mouseDragged(mEvt.getPoint());
   }

   @Override
   public void mousePressed(MouseEvent mEvt) {
      Positionable positionable = (Positionable) mEvt.getSource();
      positionable.mousePressed(mEvt.getPoint());
   }

   @Override
   public void mouseReleased(MouseEvent mEvt) {
      Positionable positionable = (Positionable) mEvt.getSource();
      positionable.mouseReleased(mEvt.getPoint());
   }
}

interface Positionable {
   void mouseDragged(Point p);

   void mousePressed(Point p);

   void mouseReleased(Point p);
}