如何在Java中绘制直线

25

我想知道Java中是否有能够从坐标(x1, x2)画到(y1, y2)的函数?

我想要做的是像这样:

drawLine(x1, x2, x3, x4);

我希望能够在代码的任何时候执行此操作,并使多行文本同时出现。

我尝试过这样做:

public void paint(Graphics g){
   g.drawLine(0, 0, 100, 100);
}
但是这种方法无法控制函数何时被使用,我也不知道如何调用它多次。 希望你能理解我的意思! P.S. 我想创建一个包含多个坐标的坐标系。

你是在使用AWT还是Swing?在当今这个时代,使用AWT几乎没有任何意义。如果使用Swing编码,请使用非“顶层”容器,如JComponentJPanel,覆盖paintComponent(Graphics)方法而不是paint(Graphics) - Andrew Thompson
我正在使用AWT,只是因为那是我最先发现的...但我会尝试你的建议。谢谢。 - Karoline Brynildsen
10个回答

41

一个非常简单的Swing组件示例,用于绘制线条。它内部保留了一个使用addLine方法添加的线条列表。每次添加新线条时,都会调用repaint来通知图形子系统需要进行新的绘画。

该类还包括一些用法示例。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class LinesComponent extends JComponent{

private static class Line{
    final int x1; 
    final int y1;
    final int x2;
    final int y2;   
    final Color color;

    public Line(int x1, int y1, int x2, int y2, Color color) {
        this.x1 = x1;
        this.y1 = y1;
        this.x2 = x2;
        this.y2 = y2;
        this.color = color;
    }               
}

private final LinkedList<Line> lines = new LinkedList<Line>();

public void addLine(int x1, int x2, int x3, int x4) {
    addLine(x1, x2, x3, x4, Color.black);
}

public void addLine(int x1, int x2, int x3, int x4, Color color) {
    lines.add(new Line(x1,x2,x3,x4, color));        
    repaint();
}

public void clearLines() {
    lines.clear();
    repaint();
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    for (Line line : lines) {
        g.setColor(line.color);
        g.drawLine(line.x1, line.y1, line.x2, line.y2);
    }
}

public static void main(String[] args) {
    JFrame testFrame = new JFrame();
    testFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    final LinesComponent comp = new LinesComponent();
    comp.setPreferredSize(new Dimension(320, 200));
    testFrame.getContentPane().add(comp, BorderLayout.CENTER);
    JPanel buttonsPanel = new JPanel();
    JButton newLineButton = new JButton("New Line");
    JButton clearButton = new JButton("Clear");
    buttonsPanel.add(newLineButton);
    buttonsPanel.add(clearButton);
    testFrame.getContentPane().add(buttonsPanel, BorderLayout.SOUTH);
    newLineButton.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            int x1 = (int) (Math.random()*320);
            int x2 = (int) (Math.random()*320);
            int y1 = (int) (Math.random()*200);
            int y2 = (int) (Math.random()*200);
            Color randomColor = new Color((float)Math.random(), (float)Math.random(), (float)Math.random());
            comp.addLine(x1, y1, x2, y2, randomColor);
        }
    });
    clearButton.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            comp.clearLines();
        }
    });
    testFrame.pack();
    testFrame.setVisible(true);
}

}

1
@Karoline:我同意您将此帖子标记为“正确”的决定。这是第一个使用Swing的帖子,很好地利用了OO(Line类),并展示了如何在单击按钮时添加和删除行。 @jassuncao:不过,有一个问题。使用LinkedList是否有任何特定原因,而不是(看似更轻)的ArrayList? - Andrew Thompson
1
在这种情况下,我们不需要 ArrayList 中快速索引的优势。添加新行是一项恒定且快速的操作,而对于具有大量元素的 ArrayList,则不能保证这一点。但主要原因是我习惯于使用链表进行计算机图形处理。这是我年轻时在 C 中开发 3D 引擎时最常用的工具。 - jassuncao
导入 java.awt.; 导入 java.awt.event.; 导入 java.util.LinkedList; 导入 javax.swing.*; - jassuncao

11

将这些行存储在某种类型的列表中。当需要绘制它们时,迭代该列表并绘制每一行。就像这样:

屏幕截图

输入图像描述

DrawLines

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.geom.Line2D;

import javax.swing.JOptionPane;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;

import java.util.ArrayList;
import java.util.Random;

class DrawLines {

    public static void main(String[] args) {

        Runnable r = new Runnable() {
            public void run() {
                LineComponent lineComponent = new LineComponent(400,400);
                for (int ii=0; ii<30; ii++) {
                    lineComponent.addLine();
                }
                JOptionPane.showMessageDialog(null, lineComponent);
            }
        };
        SwingUtilities.invokeLater(r);
    }
}

class LineComponent extends JComponent {

    ArrayList<Line2D.Double> lines;
    Random random;

    LineComponent(int width, int height) {
        super();
        setPreferredSize(new Dimension(width,height));
        lines = new ArrayList<Line2D.Double>();
        random = new Random();
    }

    public void addLine() {
        int width = (int)getPreferredSize().getWidth();
        int height = (int)getPreferredSize().getHeight();
        Line2D.Double line = new Line2D.Double(
            random.nextInt(width),
            random.nextInt(height),
            random.nextInt(width),
            random.nextInt(height)
            );
        lines.add(line);
        repaint();
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.white);
        g.fillRect(0, 0, getWidth(), getHeight());
        Dimension d = getPreferredSize();
        g.setColor(Color.black);
        for (Line2D.Double line : lines) {
            g.drawLine(
                (int)line.getX1(),
                (int)line.getY1(),
                (int)line.getX2(),
                (int)line.getY2()
                );
        }
    }
}

1
运行得非常完美!很棒的例子!虽然我将它分成了两个文件(每个类一个文件)。我还在LineComponent中添加了函数addLine(double x1, double y1, double x2, double y2),以便我可以添加自己选择的线条。 - Tihamer
尽管我已将其分成两个文件(每个类一个), 但我希望大多数人为了将[mre]中的概念适应到他们的代码库或应用程序中会进行更多的更改。唯一一个非公共类的原因就是让MRE成为可能!很高兴它有所帮助。 :) - Andrew Thompson

3
你需要创建一个继承自Component的类。在那里,你可以重写paint方法并将你的绘制代码放入其中:
package blah.whatever;

import java.awt.Component;
import java.awt.Graphics;

public class TestAWT extends Component {

/** @see java.awt.Component#paint(java.awt.Graphics) */
@Override
public void paint(Graphics g) {
    super.paint(g);
    g.drawLine(0,0,100,100);
    g.drawLine(10, 10, 20, 300);
    // more drawing code here...
}

}

将此组件放入应用程序的GUI中。如果您使用的是Swing,则需要扩展JComponent并重写paintComponent方法。
正如Helios所提到的,绘制代码实际上告诉系统您的组件看起来像什么。当系统认为需要重新绘制时(例如,如果窗口移动到您的组件前面),它将请求此信息(调用您的绘制代码)。

1
在你的类中应该拥有:
public void paint(Graphics g){
   g.drawLine(x1, y1, x2, y2);
}

然后在代码中,如果需要,您将更改x1、y1、x2、y2并调用repaint();


@Karoline:要显示多个图形,只需对它们进行迭代并绘制它们所有。 - Joeri Hendrickx

1

为了给你一些想法:

public void paint(Graphics g) {
   drawCoordinates(g);
}

private void drawCoordinates(Graphics g) {

  // get width & height here (w,h)

  // define grid width (dh, dv)

  for (int x = 0; i < w; i += dh) {
    g.drawLine(x, 0, x, h);
  }
  for (int y = 0; j < h; j += dv) {
      g.drawLine(0, y, w, y);
  }
}

我想问一下是否可以使用双精度变量而不是整数来绘制一条线。如果可以,怎么做? - MBC870
1
你做不到。例如,像素3.4这样的东西是不存在的。 - CodeGuy

1

我了解您正在使用Java AWT API进行绘图。当控件需要重新绘制时,将调用paint方法。我非常确定它在Graphics参数中提供了哪个矩形需要重新绘制(以避免全部重绘)。

因此,如果您正在呈现固定图像,则只需在该方法中绘制所需内容即可。

如果您正在进行动画处理,我假设您可以使某些区域无效,并且paint方法将自动调用。因此,您可以修改状态,调用invalidate,然后它将再次被调用。


+1 表示解释无效区域。在进行此操作时理解这个重要概念非常关键。 - Joeri Hendrickx

0
a simple line , after that you can see also a doted line 

import java.awt.*;

import javax.swing.*;

import java.awt.Graphics.*;

import java.awt.Graphics2D.*;

import javax.swing.JFrame;

import javax.swing.JPanel;

import java.awt.BasicStroke;

import java.awt.Event.*;

import java.awt.Component.*;

import javax.swing.SwingUtilities;


/**
 *
 * @author junaid
 */
public class JunaidLine extends JPanel{


//private Graphics Graphics;

private void doDrawing(Graphics g){

Graphics2D g2d=(Graphics2D) g;

float[] dash1 = {2f,0f,2f};

g2d.drawLine(20, 40, 250, 40);

BasicStroke bs1 = new BasicStroke(1,BasicStroke.CAP_BUTT,

                    BasicStroke.JOIN_ROUND,1.0f,dash1,2f);

g2d.setStroke(bs1);

g2d.drawLine(20, 80, 250, 80);

    }

@Override

public void paintComponent(Graphics g){

super.paintComponent( g);

doDrawing(g);

}


}

class BasicStrokes extends JFrame{

public  BasicStrokes(){

initUI();

}

private void initUI(){

setTitle("line");

setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

add(new JunaidLine());

setSize(280,270);

setLocationRelativeTo(null);

}

/**

* @param args the command line arguments

*/

public static void main(String[] args) {


SwingUtilities.invokeLater(new Runnable(){   

@Override

public void run(){

BasicStrokes bs = new BasicStrokes();

bs.setVisible(true);

}                

});

}


}

0
回答您最初的问题,答案是(x1, y1)(x2, y2)
例如,
这是画一条水平线:
g.drawLine( 10, 30, 90, 30 );

vs

这是绘制一条竖线的代码:

g.drawLine( 10, 30, 10, 90 );

希望能对您有所帮助。


0

您可以使用getGraphics方法来绘制您想要的组件。这将允许您绘制线条和其他可通过Graphics类获得的内容。


我只能得到对于getGraphics方法的空指针异常 :( - Karoline Brynildsen
你从哪个类中调用getGraphics方法? - nokul
你可以使用组件的getGraphics方法,但这样做是愚蠢的。Java绘图应该在paint()paintComponent()方法中按需完成。 - Andrew Thompson

0
我建立了一个完整的方法类来绘制点、线、矩形、圆等。我设计它将窗口视为一张图纸,其中原点不必在左上角,y 值随着向上移动而增加。下面是我绘制直线的方法:
public static void drawLine (double x1, double y1, double x2, double y2)
{       
    ((Graphics2D)g).draw(new Line2D.Double(x0+x1*scale, y0-y1*scale, x0+x2*scale, y0-y2*scale));
}

在上面的例子中,(x0, y0) 表示屏幕坐标系中的原点,scale 是一个缩放因子。输入参数应该作为图形坐标而不是屏幕坐标提供。没有调用 repaint()。你可以在绘制所有需要的线条之后再保存它。
我想到有人可能不想按照图纸来思考:
    ((Graphics2D)g).draw(new Line2D.Double(x1, y1, x2, y2));

请注意使用Graphics2D。这使我们可以使用双精度而不是整数来绘制Line2D对象。除了其他形状外,我的类还支持3D透视绘图和多个方便的方法(比如在给定半径的情况下画一个以某个点为中心的圆)。如果有人感兴趣,我很乐意分享更多关于这个类的内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接