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

时间:2016-04-04 20:11:20

标签: java swing

我正在开发一个绘图工具,用户可以在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像素的网格。

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

当用户在面板上绘图时,网格必须保留为背景,以便他/她可以使用它来确定以cm为单位的线条尺寸。

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

enter image description here

3 个答案:

答案 0 :(得分:1)

您可以在paintComponent()

中绘制所有单元格
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;
        }
    }
} 

答案 1 :(得分:1)

  

每个例子,网格中小方块的每一边都有5厘米的尺寸,所以当用户画一条4个方格的线时,该线的长度为20厘米。

从理论上讲,您应该可以使用Pythagorean theorem计算PPI (AKA DPI)类似

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

其中:

  • wp是宽像素数
  • hp是高像素数
  • di是屏幕的英寸对角线尺寸(即22&#34;)

例如,2560x1600 22"的屏幕的ppi为137。问题是,获得屏幕的对角线大小。

有人建议你可以使用Toolkit.getDefaultToolkit().getScreenResolution(),但这是已知的 返回一个不正确的值(即在我的屏幕上返回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或您想要的任何内容。因为它是一个已知的&#34;值,您可以相对容易地从一种分辨率转换为另一种分辨率。

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

  

谢谢你,但你的例子是关于缩放线条以在屏幕上或打印时以实际尺寸查看它们。我想要的是确定厘米到像素的比例。 (几厘米,20像素),我现在编辑问题,请检查它。

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

public static final double SCREEN_DPI = cmsToInches(20);

现在您的测量值允许每厘米20像素

20 pixels per cm

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

1cm = 20pixels,2cm = 40pixels,3cm = ......等等。我确定你可以看到数学如何适用于

答案 2 :(得分:0)

我不确定这是否是最佳解决方案,但这对我有用:

    @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。