java - 在 Jpanel 上绘制多个形状时出现歧义

标签 java swing

下面的代码有几个问题:

1) 多边形连接最后一个点和第一个点本身,不应该自己做,但用户应该绘制它。

2) 点击其他形状后多边形线条消失。

package Circles;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;

import javax.swing.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Iterator;

//////////////////////////////////////////////////////////////PaintDemo
class PaintDemo2 {
 //============================================================= main
 public static void main(String[] args) {
     PaintWindow2 window = new PaintWindow2();
     window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     window.setVisible(true);
 }//end main
}//endclass PaintDemo


@SuppressWarnings("serial")
////////////////////////////////////////////////////////////PaintWindow
class PaintWindow2 extends JFrame {
 PaintPanel2 canvas = new PaintPanel2();
 //====================================================== constructor
 public PaintWindow2() {
     //--- create the buttons
     JButton circleButton = new JButton("Circle");
     circleButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
             canvas.setShape(PaintPanel2.CIRCLE);
         }});
     JButton rectangleButton = new JButton("Rectangle");
     rectangleButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
             canvas.setShape(PaintPanel2.Ellipse);
         }});

     JButton polyButton = new JButton("Polygon");
     polyButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
             canvas.setShape(PaintPanel2.POLY);
         }});

     //--- layout the buttons
     JPanel buttonPanel = new JPanel();
     buttonPanel.setLayout(new GridLayout(2, 1));
     buttonPanel.add(circleButton);
     buttonPanel.add(rectangleButton);
     buttonPanel.add(polyButton);

     //--- layout the window
     Container content = this.getContentPane();
     content.setLayout(new BorderLayout());
     content.add(buttonPanel, BorderLayout.WEST);
     content.add(canvas     , BorderLayout.CENTER);
     this.setTitle("Paint Demo");
     this.pack();
 }//end constructor
}//endclass PaintWindow


///////////////////////////////////////////////////////////// PaintPanel2
@SuppressWarnings("serial")
class PaintPanel2 extends JPanel implements MouseListener, 
                                        MouseMotionListener {
 //--- Public constants used to specify shape being drawn.          
 public static final int NONE      = 0;
 public static final int LINE      = 1;
 public static final int Ellipse = 2;
 public static final int CIRCLE    = 3;
 public static final int POLY    = 4;

 //--- Variables to store the current figure info
 private int _shape = NONE;
 public int getShape() {
    return _shape;
}

 private int _currentStartX = 0;  // where mouse first pressed
 private int _currentStartY = 0;
 private int _currentEndX   = 0;  // where dragged to or released
 private int _currentEndY   = 0;

 //--- BufferedImage to store the underlying saved painting.
 //    Will be initialized first time paintComponent is called.
 private BufferedImage _bufImage = null;
 private boolean polygonIsNowComplete = false;

 //--- Private constant for size of paint area.
 private static final int SIZE = 600; // size of paint area

 private final Point trackPoint = new Point();
 private Path2D currentShape;
 private ArrayList<Path2D> lstPloys  = new ArrayList<Path2D>();;
 private Point lastPoint;
 private Point currentPoint;
 @SuppressWarnings("rawtypes")
private ArrayList points = new ArrayList();


 //====================================================== constructor
 public PaintPanel2() {
     setPreferredSize(new Dimension(SIZE, SIZE));
     setBackground(Color.white);
     //--- Add the mouse listeners.
     this.addMouseListener(this); 
     this.addMouseMotionListener(this);
 }//endconstructor


 //========================================================= setShape
 public void setShape(int shape) {
     //--- Provided so users can set the shape.
     _shape = shape;
 }//end setShape


 //=================================================== paintComponent
 public void paintComponent(Graphics g) {
     super.paintComponent(g);

     Graphics2D g2 = (Graphics2D)g;  // downcast to Graphics2D
     if (_bufImage == null) {
         //--- This is the first time, initialize _bufImage
         int w = this.getWidth();
         int h = this.getHeight();
         _bufImage = (BufferedImage)this.createImage(w, h);
         Graphics2D gc = _bufImage.createGraphics();
         gc.setColor(Color.white);
         gc.fillRect(0, 0, w, h); // fill in background
     }
     g2.drawImage(_bufImage, null, 0, 0);  // draw previous shapes

     drawCurrentShape(g2);
 }//end paintComponent


 //================================================= drawCurrentShape
 private void drawCurrentShape(Graphics2D g2) {
     //--- Draws current shape on a graphics context, either
     //    on the context passed to paintComponent, or the
     //    context for the BufferedImage.
     switch (_shape) {
         case NONE  :
                  break;

         case CIRCLE:
                  g2.drawOval(_currentStartX, _currentStartY,
                              _currentEndX - _currentStartX, 
                              _currentEndY - _currentStartY);
                  break;

         case Ellipse:
                  g2.draw(new Ellipse2D.Double(_currentStartX, _currentStartY,
                          _currentEndX - _currentStartX, 
                          _currentEndY - _currentStartY));
                  break;

         case POLY:

                drawPolyGon(g2);
                break;

         default:  // should never happen
                  g2.drawString("Huh?", 10, 20);
                  break;
     }
 }//end paintComponent



private void drawPolyGon(Graphics2D g2) {

    g2.create();
    if (lastPoint != null) {
         g2.setColor(Color.RED);
         g2.fillOval(lastPoint.x - 2, lastPoint.y - 2, 4, 4);
    }
    if (currentShape != null) {
         g2.setColor(Color.RED);
         g2.draw(currentShape);
         if (lastPoint != null && currentPoint != null) {
              g2.setColor(new Color(255, 0, 0, 64));
              g2.draw(new Line2D.Float(lastPoint, currentPoint));

         }
    }
    g2.setColor(Color.BLACK);
    for (Path2D shape : lstPloys) {
         g2.draw(shape);
    }
    g2.dispose();

    // TODO Auto-generated method stub

}


//===================================================== mousePressed
 public void mousePressed(MouseEvent e) {
    _currentStartX = e.getX(); // save x coordinate of the click
    _currentStartY = e.getY(); // save y
    _currentEndX   = _currentStartX;   // set end to same pixel
    _currentEndY   = _currentStartY;
 }//end mousePressed

 //===================================================== mouseDragged
 public void mouseDragged(MouseEvent e) {

  _currentEndX = e.getX();   // save new x and y coordinates
    _currentEndY = e.getY();
     this.repaint(); 
     // show new shape
 }//end mouseDragged

 //==================================================== mouseReleased
 public void mouseReleased(MouseEvent e) {
     // This will save the shape that has been dragged by
     // drawing it onto the bufferedImage where all shapes
     // are written.
    _currentEndX = e.getX(); // save ending coordinates
     _currentEndY = e.getY();

     //--- Draw the current shape onto the buffered image.
     Graphics2D grafarea = _bufImage.createGraphics();
     drawCurrentShape(grafarea);

     this.repaint();
 }//end mouseReleased


 public void mouseMoved   (MouseEvent e) {

     if (currentShape != null) {
          currentPoint = e.getPoint();
          repaint();
     } else {
          currentPoint = null;
     }


 }
 public void mouseEntered (MouseEvent e) {}
 public void mouseExited  (MouseEvent e) {}
public void mouseClicked (MouseEvent e) {
     if (e.getButton() == MouseEvent.BUTTON1) {
         if (e.getClickCount() == 1) {
              Point p = e.getPoint();
              lastPoint = p;
              if (currentShape == null) {
                   currentShape = new Path2D.Float();
                   currentShape.moveTo(p.x, p.y);
              } else {
                   currentShape.lineTo(p.x, p.y);
              }
              repaint();
         } else if (e.getClickCount() == 2) {
              currentShape.closePath();
              lstPloys.add(currentShape);
              currentShape = null;
              lastPoint = null;
              repaint();
         }
    }


 }
}

最佳答案

绘制周期是无状态的,也就是说,图形的内容不会从一个周期传递到另一个周期。

您需要在每个绘制周期重新绘制组件的全部内容。

您已尝试实现双缓冲区解决方案,但您没有传递缓冲区的图形上下文,而是传递了绘制系统提供给您的图形内容。如果您将缓冲区的图形上下文传递给 drawCurrentShape 方法,它可能会解决您的问题(并消除缓存所有形状的需要)

已更新

因此,在 PaintPanel2 组件的 paintComponent 方法中,您正在创建一个 BufferedImage,但您并未为其绘制组件...

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

    Graphics2D g2 = (Graphics2D)g;  // downcast to Graphics2D
    if (_bufImage == null) {
        //--- This is the first time, initialize _bufImage
        int w = this.getWidth();
        int h = this.getHeight();
        _bufImage = (BufferedImage)this.createImage(w, h);
        Graphics2D gc = _bufImage.createGraphics();
        gc.setColor(Color.white);
        gc.fillRect(0, 0, w, h); // fill in background
    }
    g2.drawImage(_bufImage, null, 0, 0);  // draw previous shapes

    drawCurrentShape(g2);
}//end paintComponent

相反,您应该使用从缓冲图像到 drawCurrentShape

的图形上下文
public void paintComponent(Graphics g) {
    super.paintComponent(g);

    Graphics2D g2 = (Graphics2D)g;  // downcast to Graphics2D
    Graphics2D gc = null;
    if (_bufImage == null) {
        //--- This is the first time, initialize _bufImage
        int w = this.getWidth();
        int h = this.getHeight();
        _bufImage = (BufferedImage)this.createImage(w, h);
        gc = _bufImage.createGraphics();
        gc.setColor(Color.white);
        gc.fillRect(0, 0, w, h); // fill in background
    } else {
        gc = _bufImage.createGraphics();
    }
    drawCurrentShape(g2);
    gc.dispose();

    g2.drawImage(_bufImage, null, 0, 0);  // draw previous shapes
}//end paintComponent

需要注意的是,这可能会产生一些其他问题,但这个概念是合理的。

就个人而言,我更喜欢保留所有形状的 List 并重新绘制它们。这使您能够选择、移动、删除、重新排序和更改程序中的所有形状,并提供一种历史记录 ;)

关于java - 在 Jpanel 上绘制多个形状时出现歧义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13560264/

相关文章:

java - 如何在java中将图像转换为透明图像

java - 我不确定什么是不正确的

java - 如何将多个 jComponent 插入到 jTable 的单元格中

java - JTabbedPane 中选项卡更改的 ActionLister

java - Graphics2D 渲染太慢?

java - 在没有 Web 服务器的 Spring-Boot 应用程序中,保持它运行的正确方法是什么?

java - 从网络(Twitter)读取 JSON 数据

java - JList 的 getSelectedIndex() 始终返回 -1,即使选择了一个项目

java - 为什么这个java小程序在绘制图形之前就退出了?

java - 使用日历数据更新网格布局