在 Java Swing 应用程序中向 Graphics2D 面板添加新数据

Add new data to Graphics2D panel in a Java Swing application

我正在编写一个 Java Swing 应用程序来处理数据。 我需要添加的功能之一是以图形方式可视化数据。 为此,我想使用 Graphics2D class.

我创建了一个 GUI,集成了我的程序,还有一个使用 Graphics2D 绘制图形的面板 class。

但我的问题是,在从 GUI 选择并加载文件后,我不知道如何调用 drawLine 方法

下面是简短的代码示例,显示了我的问题。 它只包含一个带有 2 个面板的基本 GUI 和一个带有加载选项的菜单来解释我的问题:

在 MyFrame.java 文件中,我在第 87 行添加了注释以准确显示我被卡住的位置。

该应用程序基于 3 个文件: main:在这里它创建了 GUI 的 MyFrame 实例 Myframe:创建 GUI 和进一步的数据处理 MyPanel:制作一个 Graphics2D 的 Jpanel,以蓝色矩形框作为起始视图。

如果有人能给我一个关于如何从 MyFrame() 构造函数外部调用此 drawLine 方法的提示...

我仍然不完全理解如何在 classes...

之间进行交互的全部要点

这是 GUI 外观的图片:

谢谢你帮我解决这个问题

public class main {

    public static void main(String[] args) {
        new MyFrame();                      
    }
}
  1. MyFrame.java:
public class MyFrame extends JFrame {

    JTextComponent tc;
    String fileName;
    
    MyFrame() {
     this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
     this.setLayout(null);
     this.setBounds(0,0,464,312);
     
     tc = new JTextPane();
     tc.setBounds(0,520,450,50);
     tc.setPreferredSize(new Dimension(450,50));
     
     JScrollPane sp = new JScrollPane(tc);
     
     JMenuBar mb = new JMenuBar();
     JMenu fm = new JMenu("File");
     JMenuItem loadItem = new JMenuItem("Load file");
       loadItem.addActionListener(e -> {tc.setText("loading"+"\n");
                                        SDprocess();});
       fm.add(loadItem);
       mb.add(fm);
       
     MyPanel p1 = new MyPanel();
       p1.setBounds(0,0,450,200);
       p1.setPreferredSize(new Dimension(450,200));
       
     JPanel p2 = new JPanel();
       p2.setBounds(0,200,450,50);
       p2.setPreferredSize(new Dimension(450,50));
       p2.add(sp);
       
     this.setJMenuBar(mb);  
     this.add(p1);
     this.add(p2);
     this.setResizable(false);
     this.setVisible(true); 
    }
    
    
    public void SDprocess() {
    
        File fr = null;
    
        JFileChooser fc = new JFileChooser();
        int result = fc.showOpenDialog(this);
       if (result == JFileChooser.APPROVE_OPTION) {
            
            fr = fc.getSelectedFile();
            fileName=fr.getName();                  
            System.out.println(fileName);
        }   
        try {
            Scanner sc = new Scanner(fr);
                tc.setText(fileName +" loading\n");
            while (sc.hasNextLine()) {
                String line = sc.nextLine();
                
            // ...
            // rest of code to get the x and y data for drawing
            // lines using  drawLine(x1,y1,x2,y2) method. 
            //
            // at this point I need to call this drawLine method but how ???
            // i just don't know how to call this method from this point and how to 
            // and update the graphics panel p1 after adding the data....
            }
        sc.close();
        } catch (IOException e) {
        e.printStackTrace();
      }
    }   
}
  1. MyPanel.java:
public class MyPanel extends JPanel {

    Graphics2D g2D;
    
    MyPanel() {
        this.setPreferredSize(new Dimension (450,200));
    }
    public void paint (Graphics g) {
        
        g2D = (Graphics2D) g;
        g2D.setStroke(new BasicStroke(1));
        g2D.setPaint(Color.blue);
        g2D.drawLine(5, 5, 445,5);
        g2D.drawLine(445, 5, 445,195);
        g2D.drawLine(445, 195, 5,195);
        g2D.drawLine(5, 195, 5,5);          
    }   
}

Oracle 有一个有用的教程,Creating a GUI With Swing。跳过 Netbeans 部分。

这是“读取文件”之前修改后的 GUI。

这是“读取文件”后修改后的 GUI。

我创建了一个应用程序模型来保存线段。这个模型传给绘图JPanel,这样就可以在绘图JPanel.

paintComponent方法中绘制线段了

我清理了你的 GUI。我使用 Swing layout managers 创建了 GUI。我将 JPanels 的创建与 JFrame 的创建分开,以便人们更容易阅读和理解代码。

这是完整的可运行代码。我在 类 内部制作了额外的 类 这样我就可以 post 将这段代码作为一个块。

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;

public class ExampleDrawingGUI {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new ExampleDrawingGUI().new MyFrame());
    }

    public class MyFrame extends JFrame {

        private static final long serialVersionUID = 1L;

        private ExampleDrawingModel model;
        
        JTextComponent tc;
        
        MyPanel p1;

        public MyFrame() {
            super("My Frame");
            this.model = new ExampleDrawingModel();
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            this.setJMenuBar(createMenuBar());
            
            p1 = new MyPanel(model);
            this.add(p1, BorderLayout.CENTER);
            this.add(createTextPanel(), BorderLayout.SOUTH);
            
            this.pack();
            this.setLocationByPlatform(true);
//          this.setResizable(false);
            this.setVisible(true);
        }
        
        private JMenuBar createMenuBar() {
            JMenuBar mb = new JMenuBar();
            JMenu fm = new JMenu("File");
            JMenuItem loadItem = new JMenuItem("Load file");
            loadItem.addActionListener(e -> {
                tc.setText("loading" + "\n");
                model.readFile();
                p1.repaint();
            });
            fm.add(loadItem);
            mb.add(fm);
            
            return mb;
        }
        
        private JPanel createTextPanel() {
            JPanel panel = new JPanel(new BorderLayout());
            panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
            
            tc = new JTextPane();
            tc.setPreferredSize(new Dimension(450, 50));
            JScrollPane sp = new JScrollPane(tc);
            panel.add(sp, BorderLayout.CENTER);
            
            return panel;
        }
        
        public void repaint() {
            p1.repaint();
        }

    }

    public class MyPanel extends JPanel {

        private static final long serialVersionUID = 1L;
        
        private ExampleDrawingModel model;

        public MyPanel(ExampleDrawingModel model) {
            this.model = model;
            this.setPreferredSize(new Dimension(450, 200));
        }

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

            Graphics2D g2D = (Graphics2D) g;
            paintBorder(g2D);
            
            for (LineSegment line : model.getLines()) {
                Point startPoint = line.getStartPoint();
                Point endPoint = line.getEndPoint();
                g2D.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
            }
        }

        private void paintBorder(Graphics2D g2D) {
            int margin = 5;
            int x1 = margin;
            int x2 = getWidth() - margin;
            int y1 = margin;
            int y2 = getHeight() - margin;
            
            g2D.setStroke(new BasicStroke(3f));
            g2D.setPaint(Color.blue);
            g2D.drawLine(x1, y1, x1, y2);
            g2D.drawLine(x1, y1, x2, y1);
            g2D.drawLine(x2, y1, x2, y2);
            g2D.drawLine(x1, y2, x2, y2);
        }

    }
    
    public class ExampleDrawingModel {
        
        private List<LineSegment> lines;
        
        public ExampleDrawingModel() {
            this.lines = new ArrayList<>();
        }
        
        public void readFile() {
            this.lines.clear();
            // Here's where you'd read a file and create a list of lines.
            lines.add(new LineSegment(new Point(100, 100), new Point(100, 150)));
        }

        public List<LineSegment> getLines() {
            return lines;
        }
        
    }
    
    public class LineSegment {
        
        private final Point startPoint, endPoint;

        public LineSegment(Point startPoint, Point endPoint) {
            this.startPoint = startPoint;
            this.endPoint = endPoint;
        }

        public Point getStartPoint() {
            return startPoint;
        }

        public Point getEndPoint() {
            return endPoint;
        }
        
    }

}

首先,您的代码存在一些问题:

class MyFrame extends JFrame {

    //....

    MyFrame() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLayout(null);  // !! Good God, no don't do this!
        this.setBounds(0, 0, 464, 312); // and don't do this

像瘟疫一样避免空布局和 setBounds,因为这会导致 GUI 非常不灵活,虽然它们在一个平台上看起来不错,但在大多数其他平台或屏幕分辨率上看起来很糟糕,而且很难更新和维护。相反,您需要研究和学习布局管理器,然后嵌套 JPanel,每个 JPanel 都使用自己的布局管理器来创建令人愉悦且复杂的 GUI,这些 GUI 在所有 OS 上看起来都不错。

此外,不要忘记在 添加组件之后和设置可见之前对 JFrame 调用 pack(),以便布局管理器执行他们的操作。

然后:

class MyPanel extends JPanel {
    // ...

    Graphics2D g2D; //!! -- no, don't do this

如果您创建一个 Graphics 或 Graphics2D 字段,您很想在绘画方法之外使用它,这是灾难的根源,因为从组件获得的任何 Graphics 都是短暂的,并且这样做有创建脆弱的风险图形或抛出 NullPointerException

    public void paint (Graphics g) {
        
        g2D = (Graphics2D) g;
        g2D.setStroke(new BasicStroke(1));
        g2D.setPaint(Color.blue);
        g2D.drawLine(5, 5, 445,5);
        g2D.drawLine(445, 5, 445,195);
        g2D.drawLine(445, 195, 5,195);
        g2D.drawLine(5, 195, 5,5);          
    }

不要重写 paint 而是重写 paintComponent,因为这风险较小(paint 有更大的责任,你不想搞砸)并且如果需要的话动画更流畅,因为 paintComponent 默认使用双缓冲。

此外,您几乎总是 应该在您自己的重写中调用 super 的绘画方法,因此改为:

    @Override
    protected void paintComponent(Graphics g) {
        // first call the super's method:
        super.paintComponent(g);
        Graphics2D g2D = (Graphics2D) g.create();       

现在关于您的实际问题,即在呈现 GUI 之后创建图像和绘图,可能最简单的方法是创建一个 BufferedImage 并在您的 GUI 中使用它绘制。您可以通过调用 Graphics 方法 drawImage(...) 轻松完成此操作。您可以在需要时随时将 BufferedImage 传递到您的绘图 JPanel 中。例如,您的代码可能类似于...

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;

import javax.swing.*;

public class Foo {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {          
            JFrame frame = new JFrame("GUI");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            
            MainPanel mainPanel = new MainPanel();
            MyMenu myMenu = new MyMenu();
            myMenu.setMyPanel(mainPanel.getMyPanel());
            myMenu.setMainPanel(mainPanel);
            
            frame.add(mainPanel);
            frame.setJMenuBar(myMenu);
            frame.pack();
            frame.setResizable(false);
            frame.setLocationRelativeTo(null);  
            frame.setVisible(true);
        });
    }
}
class MainPanel extends JPanel {
    private static final int GAP = 5;
    private JTextArea textArea = new JTextArea(4, 40);
    private MyPanel myPanel = new MyPanel();
    
    public MainPanel() {
        textArea.setFocusable(false);
        JScrollPane scrollPane = new JScrollPane(textArea);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        
        setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
        setLayout(new BorderLayout(GAP, GAP));
        add(myPanel);
        add(scrollPane, BorderLayout.PAGE_END);
    }
    
    public MyPanel getMyPanel() {
        return myPanel;
    }

    public void appendTextAreaText(String text) {
        textArea.append(text);
    }
    
    public void setBuffImg(BufferedImage bImage) {
        myPanel.setBuffImg(bImage);
    }
}
class MyMenu extends JMenuBar {
    private MainPanel mainPanel;
    private MyPanel myPanel;
    
    public MyMenu() {
        JMenu fm = new JMenu("File");
        JMenuItem loadItem = new JMenuItem("Load file");
        loadItem.addActionListener(e -> {
            // Emulate reading file here in a background thread
            
            if (myPanel != null) {
                int width = MyPanel.MY_WIDTH;
                int height = MyPanel.MY_HEIGHT;
                BufferedImage bImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

                Graphics2D g2 = bImg.createGraphics();
                
                // draw with g2 here using data from file

                // emulating this:
                g2.setColor(Color.RED);
                
                float strokeWidth = (float) (2 + 6 * Math.random());
                g2.setStroke(new BasicStroke(strokeWidth));
                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                
                int x2 = (int) (MyPanel.MY_WIDTH * (1 + Math.random()) / 2);
                int y2 = (int) (MyPanel.MY_HEIGHT * (1 + Math.random()) / 2);
                g2.drawLine(5, 5, x2, y2);
                
                g2.dispose();
                
                myPanel.setBuffImg(bImg);
                
                if (mainPanel != null) {
                    mainPanel.appendTextAreaText("adding image \n");
                }
                
            }
        });
        fm.add(loadItem);
        add(fm);        
    }
    
    public void setMyPanel(MyPanel myPanel) {
        this.myPanel = myPanel;
    }
    
    public void setMainPanel(MainPanel mainPanel) {
        this.mainPanel = mainPanel;
    }
    
    
}

class MyPanel extends JPanel {
    private static final int GAP = 5;
    public static final int MY_WIDTH = 450;
    public static final int MY_HEIGHT = 200;
    private BufferedImage bImg = null;

    // Graphics2D g2D; //!! -- no, never do this!!

    MyPanel() {
        // this.setPreferredSize(new Dimension(450, 200));
        setBackground(Color.WHITE);
    }
    
    // better to override getPreferredSize
    @Override
    public Dimension getPreferredSize() {
        return new Dimension(MY_WIDTH + 2 * GAP, MY_HEIGHT + 2 * GAP);
    }
    
    public void setBuffImg(BufferedImage bImg) {
        this.bImg = bImg;
        repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        // first call the super's method:
        super.paintComponent(g);
        if (bImg != null) {
            g.drawImage(bImg, GAP, GAP, null);
        }

        Graphics2D g2D = (Graphics2D) g.create();
        g2D.setStroke(new BasicStroke(1));
        g2D.setPaint(Color.blue);
        Rectangle rect = new Rectangle(GAP, GAP, getWidth() - 2 * GAP, getHeight() - 2 * GAP);
        g2D.draw(rect);
    }

    // Don't override paint but rather paintComponent
    // public void paint (Graphics g) {

}