如何使用Java Swing绘制二维箭头?

如何使用Java Swing绘制二维箭头?,java,swing,paintcomponent,jslider,Java,Swing,Paintcomponent,Jslider,我试图用Swing 2d绘制一些可旋转箭头,网上有一些示例代码,所以我将它们复制并组合到一个应用程序中,但这三种方法中的每一种都有问题:第一种方法没有从中心旋转,另两种方法没有正确地查看箭头,有人能告诉我如何修复它们吗 import java.awt.*; import java.awt.geom.*; import javax.swing.*; import javax.swing.event.*; public class Arrow_Test extends JPanel impleme

我试图用Swing 2d绘制一些可旋转箭头,网上有一些示例代码,所以我将它们复制并组合到一个应用程序中,但这三种方法中的每一种都有问题:第一种方法没有从中心旋转,另两种方法没有正确地查看箭头,有人能告诉我如何修复它们吗

import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;

public class Arrow_Test extends JPanel implements ChangeListener {
    Path2D.Double arrow = createArrow();
    double theta = 0;

    public void stateChanged(ChangeEvent e) {
        int value = ((JSlider) e.getSource()).getValue();
        theta = Math.toRadians(value);
        repaint();
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setStroke(new BasicStroke(6));
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        int cx = getWidth() / 2;
        int cy = getHeight() / 2;
        AffineTransform at = AffineTransform.getTranslateInstance(cx, cy);
        at.rotate(theta);
        at.scale(2.0, 2.0);
        Shape shape = at.createTransformedShape(arrow);
        g2.setPaint(Color.blue);
        g2.draw(shape);

        GeneralPath a = drawArrow(20, 20, 30, 20, 50, 13);
        AffineTransform at1 = AffineTransform.getTranslateInstance(cx, cy);
        at1.rotate(theta);
        at1.scale(2.0, 2.0);
        Shape shape1 = at1.createTransformedShape(a);
        g2.setPaint(Color.blue);
        g2.draw(shape1);

        drawArrow(100, 100, 50, 0, g2);
    }

    private Path2D.Double createArrow() {
        int length = 80;
        int barb = 15;
        double angle = Math.toRadians(20);
        Path2D.Double path = new Path2D.Double();
        path.moveTo(-length / 2, 0);
        path.lineTo(length / 2, 0);
        double x = length / 2 - barb * Math.cos(angle);
        double y = barb * Math.sin(angle);
        path.lineTo(x, y);
        x = length / 2 - barb * Math.cos(-angle);
        y = barb * Math.sin(-angle);
        path.moveTo(length / 2, 0);
        path.lineTo(x, y);
        return path;
    }

    GeneralPath drawArrow(int x1, int y1, int x2, int y2, double length,
            double width) {
        double x, y;
        length = 50;
        width = 5;

        Point2D start = new Point2D.Double(x1, y1);
        Point2D end = new Point2D.Double(x2, y2);
        Point2D base = new Point2D.Double();
        Point2D back1 = new Point2D.Double();
        Point2D back2 = new Point2D.Double();

        length = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));

        // Compute normalized line vector
        x = (x2 - x1) / length;
        y = (y2 - y1) / length;
        // Compute points for arrow head
        base.setLocation(x2 - x * length, y2 - y * length);
        back1.setLocation(base.getX() - width * y, base.getY() + width * x);
        back2.setLocation(base.getX() + length * y, base.getY() - width * x);

        Line2D.Double l1 = new Line2D.Double(start, end);
        Line2D.Double l2 = new Line2D.Double(end, back2);
        Line2D.Double l3 = new Line2D.Double(end, back1);

        GeneralPath c = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
        c.append(l1, true);
        c.append(l2, true);
        c.append(l3, true);

        return c;
    }

    private void drawArrow(int x1, int y1, int x2, int y2, Graphics2D g2d) {

        int x[] = { 0, 36, 0 };
        int y[] = { -10, 0, 10 };

        g2d.rotate(theta);
        g2d.drawLine(x1 - 20, y1, x1 + 20, y1);
        // will move the orgin
        g2d.translate(x1, y1);
        double angle = findLineAngle(x1 - 20, y1, x1 + 20, y1);
        System.out.println("angle is===>" + angle);
        g2d.rotate(angle);

        g2d.fillPolygon(new Polygon(x, y, 3));
        // /will restore orgin
        g2d.translate(-x2, -y2);
        g2d.rotate(-angle);
    }

    private double findLineAngle(int x1, int y1, int x2, int y2) {
        if ((x2 - x1) == 0)
            return Math.PI / 2;
        return Math.atan((y2 - y1) / (x2 - x1));
    }

    private JSlider getSlider() {
        JSlider slider = new JSlider(-180, 180, 0);
        slider.addChangeListener(this);
        return slider;
    }

    public static void main(String[] args) {
        Arrow_Test test = new Arrow_Test();
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(test);
        f.add(test.getSlider(), "Last");
        f.setSize(400, 400);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

我真的不想进入“为什么”,因为你的代码很难阅读

旋转对象时,应指定旋转所围绕的锚点(x/y)。默认情况下,这是当前上下文的
0x0
位置

为什么你的基于“路径”的箭头看起来…很有趣,可能与它们的创建方式有关,但我并没有真正玩弄它们

你需要注意的另一件事是,转换是复合的,这是一件好事,也是一件坏事,你只需要小心处理它们;)

让我们从一个基本形状开始

public class Arrow extends Path2D.Double {

    public Arrow() {
        moveTo(0, 10);
        lineTo(36, 10);
        moveTo(36 - 16, 0);
        lineTo(36, 10);
        moveTo(36 - 16, 20);
        lineTo(36, 10);
    }

}
好的,没有什么令人印象深刻的,您可以添加宽度/高度参数,使箭头以您想要的方式显示,但这有一个基本的开始。我更喜欢使用基于
Shape
的对象,它们比旧的
Polygon
风格的API更易于使用

箭头基本上是三条线,它们在垂直线的中间和水平线的末端相交。如果箭头是一条直线,你可能会得到更好的结果,但我会把它留给你去玩

接下来,我们需要定位和旋转对象(
arrow
arrow
BTW的一个实例)

我们首先应用一个转换,这使得
Graphics
上下文的
0x0
位置现在是我们指定的
x/y
位置。这使得计算箭头应围绕其旋转的锚定位置变得更加容易

以及一个可运行的示例,将所有内容绑定在一起

import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Arrow_Test extends JPanel implements ChangeListener {

    double theta = 0;
    Path2D arrow = new Arrow();

    public void stateChanged(ChangeEvent e) {
        int value = ((JSlider) e.getSource()).getValue();
        theta = Math.toRadians(value);
        repaint();
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;

        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);

        g2d.setStroke(new BasicStroke(4));

        double x = (getWidth() - arrow.getBounds().getWidth()) / 2d;
        double y = (getHeight() - arrow.getBounds().getHeight()) / 2d;

        AffineTransform at = new AffineTransform();
        at.translate(x, y);
        at.rotate(theta, arrow.getBounds().getWidth() / 2d, arrow.getBounds().getHeight() / 2d);
        g2d.setTransform(at);

        g2d.draw(arrow);
        g2d.dispose();
    }

    private JSlider getSlider() {
        JSlider slider = new JSlider(-180, 180, 0);
        slider.addChangeListener(this);
        return slider;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                Arrow_Test test = new Arrow_Test();
                JFrame f = new JFrame();
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.add(test);
                f.add(test.getSlider(), "Last");
                f.setSize(400, 400);
                f.setLocationRelativeTo(null);
                f.setVisible(true);
            }
        });
    }

    public class Arrow extends Path2D.Double {

        public Arrow() {
            moveTo(0, 10);
            lineTo(36, 10);
            moveTo(36 - 16, 0);
            lineTo(36, 10);
            moveTo(36 - 16, 20);
            lineTo(36, 10);
        }

    }
}


这个问题读起来可能太像“请修复我找到的代码”。为了改善这一点,考虑告诉我们你已经发现的概念,你是如何试图合并和修复它们的,以及它们是如何对你不起作用的。换言之,考虑向我们展示你努力写自己的箭的成果,基于借用的概念而不是借用的代码。小心,变换是复合的。
import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Arrow_Test extends JPanel implements ChangeListener {

    double theta = 0;
    Path2D arrow = new Arrow();

    public void stateChanged(ChangeEvent e) {
        int value = ((JSlider) e.getSource()).getValue();
        theta = Math.toRadians(value);
        repaint();
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;

        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);

        g2d.setStroke(new BasicStroke(4));

        double x = (getWidth() - arrow.getBounds().getWidth()) / 2d;
        double y = (getHeight() - arrow.getBounds().getHeight()) / 2d;

        AffineTransform at = new AffineTransform();
        at.translate(x, y);
        at.rotate(theta, arrow.getBounds().getWidth() / 2d, arrow.getBounds().getHeight() / 2d);
        g2d.setTransform(at);

        g2d.draw(arrow);
        g2d.dispose();
    }

    private JSlider getSlider() {
        JSlider slider = new JSlider(-180, 180, 0);
        slider.addChangeListener(this);
        return slider;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                Arrow_Test test = new Arrow_Test();
                JFrame f = new JFrame();
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.add(test);
                f.add(test.getSlider(), "Last");
                f.setSize(400, 400);
                f.setLocationRelativeTo(null);
                f.setVisible(true);
            }
        });
    }

    public class Arrow extends Path2D.Double {

        public Arrow() {
            moveTo(0, 10);
            lineTo(36, 10);
            moveTo(36 - 16, 0);
            lineTo(36, 10);
            moveTo(36 - 16, 20);
            lineTo(36, 10);
        }

    }
}