How to draw 2d arrows with Java Swing?

岁酱吖の 提交于 2019-12-02 13:10:28

I really don't want to get into "why", as your code is hard enough to read.

When rotating an object, you should specify a anchor (x/y) around which the rotation should take place. By default, this is the 0x0 position of the current context.

Why your "path" based arrows look...interesting, could have to do with the way they are created, but I didn't really play around with them.

The other thing you need to be careful is, transformations are compounding, this is a good and bad thing, you just need to be careful with them ;)

Lets start with a basic shape...

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);
    }

}

Okay, nothing impressive, you could add width/height parameters to make the arrow appear the way you want, but this gets a basic start. I prefer to use Shape based objects, they are just simpler to work with then the old Polygon style API.

The Arrow is basically three lines which meet at the middle of the vertical and the end of the horizontal. You might get a better result if the arrow head was a single line, but I'll leave that for you to play with

Next, we need to position and rotate the object (arrow is an instance of Arrow BTW)

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);

We apply a translation first, this makes it so that the Graphics context's 0x0 position is now the x/y position we specify. This makes it MUCH easier to calculate the anchor position around which the arrow should rotate...

And a runnable example to bind it all together

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);
        }

    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!