2016-04-04 61 views
0

我正在開發一個繪圖工具,用戶可以在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

+0

讓網格成爲一個特定的大小將需要一些工作。我似乎記得UI的渲染速度是96dpi,但可能已經改變了 – MadProgrammer

+0

@MadProgrammer:謝謝,那麼你有什麼建議嗎? –

+0

我一直試圖通過這個,但獲得屏幕PPI的基本計算涉及'sqrt(寬度^ 2 +高度^ 2)/對角線監視器大小(以英寸)',這是最後一部分證明很難找到。所以'2560x1600'和'22''屏幕的PPI爲'137' – MadProgrammer

回答

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; 
     } 
    } 
} 
+0

我的解決方案是將所有以前的行保存在arraylist中,並將它們全部繪製在'paintComponent '。請檢查我的更新示例。 – rdonuk

+0

好的答案!謝謝 –

+0

您能否提供簡單的代碼讓用戶點擊所需的行然後使用窗口中的按鈕將其刪除?或者我應該向此發送另一個問題?謝謝 –

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。

1

每個示例網格中小方塊的每一邊都有5釐米的尺寸,所以當用戶繪製一條佔用4個方塊的線時,線的長度將爲20釐米。

從理論上講,你應該能夠使用Pythagorean theorem計算肚裏的PPI (AKA DPI)

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

其中:

  • WP是像素寬
  • 馬力數是高像素的數量
  • di是以英寸爲單位的對角線尺寸È屏幕(即22" )

例如,通過2560x1600一個22"屏幕給出137一個PPI。問題是,獲得屏幕的對角線尺寸。

有人建議,你可以使用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 S的繪製網格溢出視圖的可視範圍...

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匹配和看看結果;)

謝謝你,但你的例子是關於縮放線實時查看它們屏幕上或打印時的尺寸。我想要的是確定像素的釐米級別。 (20像素幾釐米),我現在編輯問題,請檢查它。

那麼,改變你的SCREEN_DPI匹配,例如

public static final double SCREEN_DPI = cmsToInches(20); 

現在你有一個測量,這將允許每平方釐米20個像素

20 pixels per cm

基本上你需要知道有多少像素代表釐米,然後您可以簡單地從釐米轉換爲像素,然後再轉回...

1cm = 20像素,2cm = 40像素,3cm = ...等等。我相信你可以看到數學如何工作

+0

謝謝你,但你的例子是關於縮放線條以便在屏幕上或打印時以實際尺寸查看它們,我想要確定一個釐米到像素的比例(20釐米的幾釐米),我編輯 –

+0

@JadChahine好的,所以你的問題要簡單得多,你只需要決定有多少像素代表一釐米,例如,如果你想要20像素p呃釐米,那麼1釐米= 20像素,2釐米= 40像素...我現在很確定你可以看到轉換是如何工作的 – MadProgrammer

相關問題