有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何在绘制线条时向JPanel添加背景网格?

我正在开发一个绘图工具,用户可以在JPanel上画一条线

他将选择一个起点,然后将这条线拖动到终点以创建这条线

请注意,这里使用Java Point类来定义每个点的坐标

下面是此场景中使用的简单代码:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class DrawLine extends JPanel {

    private MouseHandler mouseHandler = new MouseHandler();
    private Point p1 = new Point(0, 0);
    private Point p2 = new Point(0, 0);
    private boolean drawing;

    public DrawLine() {
        this.setPreferredSize(new Dimension(400, 200));
        this.addMouseListener(mouseHandler);
        this.addMouseMotionListener(mouseHandler);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.blue);
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setStroke(new BasicStroke(8,
            BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
        g.drawLine(p1.x, p1.y, p2.x, p2.y);
    }

    private class MouseHandler extends MouseAdapter {

        @Override
        public void mousePressed(MouseEvent e) {
            drawing = true;
            p1 = e.getPoint();
            p2 = p1;
            repaint();
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            drawing = false;
            p2 = e.getPoint();
            repaint();
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            if (drawing) {
                p2 = e.getPoint();
                repaint();
            }
        }
    }

    private void display() {
        JFrame f = new JFrame("LinePanel");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new DrawLine().display();
            }
        });
    }
} 

我想做的是画一个每平方20像素的网格

用户将选择以厘米为单位的比例,例如,每20个像素将为50厘米

当用户在面板上绘图时,网格必须作为背景,以便他/她可以使用它来确定线的尺寸(以厘米为单位)

为了更清楚,在C#中,我使用了一个picturebox,分配了一个网格的背景图像,并用于在其上绘制,如下图所示:

enter image description here


共 (3) 个答案

  1. # 1 楼答案

    你可以在^{中绘制所有单元格

    import java.awt.BasicStroke;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Point;
    import java.awt.RenderingHints;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.util.ArrayList;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class DrawLine extends JPanel {
    
        private MouseHandler mouseHandler = new MouseHandler();
        private Point p1 = new Point(0, 0);
        private Point p2 = new Point(0, 0);
        private boolean drawing;
    
        //Store lines in an arraylist
        private ArrayList<Line> lines = new ArrayList<>();
    
        public DrawLine() {
            setBackground(Color.white);
            this.setPreferredSize(new Dimension(400, 200));
            this.addMouseListener(mouseHandler);
            this.addMouseMotionListener(mouseHandler);
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
    
            //Grid start
            g.setColor(Color.lightGray);
            int sideLength = 20;
            int nRowCount = getHeight() / sideLength;
            int currentX = sideLength;
            for (int i = 0; i < nRowCount; i++) {
                g.drawLine(0, currentX, getWidth(), currentX);
                currentX = currentX + sideLength;
            }
    
            int nColumnCount = getWidth() / sideLength;
            int currentY = sideLength;
            for (int i = 0; i < nColumnCount; i++) {
                g.drawLine(currentY, 0, currentY, getHeight());
                currentY = currentY + sideLength;
            }
            //Grid end
    
            Graphics2D g2d = (Graphics2D) g;
            g2d.setColor(Color.blue);
            g2d.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setStroke(new BasicStroke(8,
                BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
            g.drawLine(p1.x, p1.y, p2.x, p2.y);
    
            //draw all previous lines
            for (int i = 0; i < lines.size(); i++) { 
                g.drawLine(lines.get(i).p1.x, lines.get(i).p1.y, lines.get(i).p2.x, lines.get(i).p2.y);
            }
        }
    
        private class MouseHandler extends MouseAdapter {
    
            @Override
            public void mousePressed(MouseEvent e) {
                drawing = true;
                p1 = e.getPoint();
                p2 = p1;
                repaint();
            }
    
            @Override
            public void mouseReleased(MouseEvent e) {
                drawing = false;
                p2 = e.getPoint();
                repaint();
                lines.add(new Line(p1, p2));
            }
    
            @Override
            public void mouseDragged(MouseEvent e) {
                if (drawing) {
                    p2 = e.getPoint();
                    repaint();
                }
            }
        }
    
        private void display() {
            JFrame f = new JFrame("LinePanel");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.add(this);
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    new DrawLine().display();
                }
            });
        }
    
        public class Line {
            Point p1;
            Point p2;
    
            public Line(Point p1, Point p2) {
                this.p1 = p1;
                this.p2 = p2;
            }
        }
    } 
    
  2. # 2 楼答案

    我不确定这是否是最好的解决方案,但这对我很有效:

        @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
    
        // ADDED CODE  - draws horizontal lines  - //
        for (int i = 1; i < 500; i += 10) {
            g.drawLine(i, 1, i, 500);
        }
    
        // ADDED CODE  - draws vertical lines  - //
        for (int i = 1; i < 500; i += 10) {
            g.drawLine(1, i, 500, i);
        }
    
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.blue);
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setStroke(new BasicStroke(8,
            BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
    
        g.drawLine(p1.x, p1.y, p2.x, p2.y);
    }
    

    与其将500硬编码为循环的上限,不如将其作为JPanel的maxWidth和maxHeight

  3. # 3 楼答案

    Per example each side of the little square in the grid will have a dimension of 5 cm , so when the user draw a line that takes 4 squares, the line will have a length of 20 cm.

    理论上,你应该能够使用Pythagorean theorem来计算PPI (AKA DPI),它的变化如下

    ppi = sqrt(wp^2+hp^2) / di
    

    其中:

    • wp是宽度的像素数
    • hp是高像素数
    • di是屏幕的对角线尺寸,单位为英寸(即22英寸)

    例如,通过22"2560x1600屏幕给出137的ppi。问题是,获取屏幕的对角线大小

    有人建议你可以使用^{,但这是众所周知的 返回一个不正确的值(即在我的屏幕上,它返回102,而它应该返回137

    那么,该怎么办?当其他方法失败时,假装失败

    下面的示例定义了一个96的DPI(这是一个非常古老的通用屏幕分辨率,Java/Swing使用它是基于这个分辨率的,不能说它现在是或不是,但它过去是)

    下面定义了5cmx5cm单元格的网格,总宽度/高度为20cm

    Grid

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.DisplayMode;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GraphicsDevice;
    import java.awt.GraphicsEnvironment;
    import java.awt.Toolkit;
    import java.awt.geom.Rectangle2D;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        // The number of CMs per Inch
        public static final double CM_PER_INCH = 0.393700787d;
        // The number of Inches per CMs
        public static final double INCH_PER_CM = 2.545d;
        // The number of Inches per mm's
        public static final double INCH_PER_MM = 25.45d;
    
        /**
         * Converts the given pixels to cm's based on the supplied DPI
         *
         * @param pixels
         * @param dpi
         * @return
         */
        public static double pixelsToCms(double pixels, double dpi) {
            return inchesToCms(pixels / dpi);
        }
    
        /**
         * Converts the given cm's to pixels based on the supplied DPI
         *
         * @param cms
         * @param dpi
         * @return
         */
        public static double cmsToPixel(double cms, double dpi) {
            return cmToInches(cms) * dpi;
        }
    
        /**
         * Converts the given cm's to inches
         *
         * @param cms
         * @return
         */
        public static double cmToInches(double cms) {
            return cms * CM_PER_INCH;
        }
    
        /**
         * Converts the given inches to cm's
         *
         * @param inch
         * @return
         */
        public static double inchesToCms(double inch) {
            return inch * INCH_PER_CM;
        }
    
        public static final double SCREEN_DPI = 72.0;
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            public TestPane() {
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension((int)Math.round(cmsToPixel(20d, SCREEN_DPI)), (int)Math.round(cmsToPixel(20d, SCREEN_DPI)));
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                double cellSize = cmsToPixel(5d, SCREEN_DPI);
                Rectangle2D cell = new Rectangle2D.Double(0, 0, cellSize, cellSize);
    
                g2d.setColor(Color.LIGHT_GRAY);
                double x = 0;
                double y = 0;
                //System.out.println("Columns = " + (getWidth() / cellSize));
                //System.out.println("Rows = " + (getHeight()/ cellSize));
                while (y + cellSize < getHeight()) {
                    x = 0;
                    while (x + cellSize < getWidth()) {
                        g2d.translate(x, y);
                        g2d.draw(cell);
                        g2d.translate(-x, -y);
                        x += cellSize;
                    }
                    y += cellSize;
                }
    
                g2d.dispose();
            }
    
        }
    }
    

    您可能会注意到,当您调整窗口大小时,网格不会更新,直到有足够的空间容纳新行/列,这是为了演示算法

    通过更改绘制网格的while-loop,可以允许网格溢出视图的可见边界

    while (y < getHeight()) {
        x = 0;
        while (x < getWidth()) {
            g2d.translate(x, y);
            g2d.draw(cell);
            g2d.translate(-x, -y);
            x += cellSize;
        }
        y += cellSize;
    }
    

    这一切的关键是,你不能把SCREEN_DPI变成你想要的任何东西。要进行打印预览,请将其更改为30072或任何您想要的内容。因为它是一个“已知”值,所以可以相对容易地从一种分辨率转换为另一种分辨率

    下面是一个想法,计算屏幕的PPI,并更改SCREEN_DPI以匹配并查看结果;)

    Thank you but your example is about scaling the lines to view them in real dimensions on the screen or upon printing. What I want is to determine a scale of centimeters to pixels. (several cm for 20 px), I edit the question now please check it.

    所以呢?例如,将SCREEN_DPI更改为匹配

    public static final double SCREEN_DPI = cmsToInches(20);
    

    现在你有了一个每厘米20像素的测量

    20 pixels per cm

    基本上你需要知道多少像素代表一个厘米,然后你可以简单地从厘米转换为像素,然后再转换回来

    1厘米=20像素,2厘米=40像素,3厘米=。。。诸如此类。我相信你可以看到数学是如何运作的