Как нарисовать направленную стрелку в Java?

Я хочу нарисовать направленную стрелку через Java.

В настоящее время я использую класс java.awt.Line2D.Double для рисования линии

g2.setStroke(new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); // g2 is an instance of Graphics2D
g2.draw(new Line2D.Double(x1,y1,x2,y2));

Но появляется только линия, а направленная стрелка не появляется. BasicStroke.Join_BEVEL используется для рисования направленной стрелки. Он применяется, когда встречаются два отрезка линии.

Линия, которую я рисую, пересекает границу прямоугольника, но направленная стрелка не рисуется. Рисуется только простая линия.

Есть ли что-то, что мне не хватает?


person Yatendra    schedule 08.01.2010    source источник


Ответы (5)


Скос рисуется между сегментами полилинии, если они находятся под определенными углами. Это не имеет значения, если вы рисуете линию, которая оказывается нарисованной рядом с некоторыми другими пикселями определенного цвета - после того, как вы нарисовали прямоугольник, объект Graphics не знает о прямоугольнике, он (фактически) держит только пиксели. (точнее, изображение или окно ОС содержит пиксели).

Чтобы нарисовать простую стрелку, нарисуйте линию для стебля, как вы это делаете, затем полилинию для V-образной формы. Более красивые стрелки имеют изогнутые стороны и заполнены.

Вы, вероятно, не хотите использовать скос для наконечника стрелки, так как скосы являются плоскими; вместо этого используйте параметр митры:

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

public class BevelArrows
{
    public static void main ( String...args )
    {
        SwingUtilities.invokeLater ( new Runnable () {
            BevelArrows arrows = new BevelArrows();

            @Override
            public void run () {
                JFrame frame = new JFrame ( "Bevel Arrows" );

                frame.add ( new JPanel() {
                    public void paintComponent ( Graphics g ) {
                        arrows.draw ( ( Graphics2D ) g, getWidth(), getHeight() );
                    }
                }
                , BorderLayout.CENTER );

                frame.setSize ( 800, 400 );
                frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
                frame.setVisible ( true );
            }
        } );
    }

    interface Arrow {
        void draw ( Graphics2D g );
    }

    Arrow[] arrows = { new LineArrow(), new CurvedArrow() };

    void draw ( Graphics2D g, int width, int height )
    {
        g.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );

        g.setColor ( Color.WHITE );
        g.fillRect ( 0, 0, width, height );

        for ( Arrow arrow : arrows ) {
            g.setColor ( Color.ORANGE );
            g.fillRect ( 350, 20, 20, 280 );

            g.setStroke ( new BasicStroke ( 20.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL ) );
            g.translate ( 0, 60 );
            arrow.draw ( g );

            g.setStroke ( new BasicStroke ( 20.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER ) );
            g.translate ( 0, 100 );
            arrow.draw ( g );

            g.setStroke ( new BasicStroke ( 20.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND ) );
            g.translate ( 0, 100 );
            arrow.draw ( g );

            g.translate ( 400, -260 );
        }
    }

    static class LineArrow  implements Arrow
    {
        public void draw ( Graphics2D g )
        {
            // where the control point for the intersection of the V needs calculating
            // by projecting where the ends meet

            float arrowRatio = 0.5f;
            float arrowLength = 80.0f;

            BasicStroke stroke = ( BasicStroke ) g.getStroke();

            float endX = 350.0f;

            float veeX;

            switch ( stroke.getLineJoin() ) {
                case BasicStroke.JOIN_BEVEL:
                    // IIRC, bevel varies system to system, this is approximate
                    veeX = endX - stroke.getLineWidth() * 0.25f;
                    break;
                default:
                case BasicStroke.JOIN_MITER:
                    veeX = endX - stroke.getLineWidth() * 0.5f / arrowRatio;
                    break;
                case BasicStroke.JOIN_ROUND:
                    veeX = endX - stroke.getLineWidth() * 0.5f;
                    break;
            }

            // vee
            Path2D.Float path = new Path2D.Float();

            path.moveTo ( veeX - arrowLength, -arrowRatio*arrowLength );
            path.lineTo ( veeX, 0.0f );
            path.lineTo ( veeX - arrowLength, arrowRatio*arrowLength );

            g.setColor ( Color.BLUE );
            g.draw ( path );

            // stem for exposition only
            g.setColor ( Color.YELLOW );
            g.draw ( new Line2D.Float ( 50.0f, 0.0f, veeX, 0.0f ) );

            // in practice, move stem back a bit as rounding errors
            // can make it poke through the sides of the Vee
            g.setColor ( Color.RED );
            g.draw ( new Line2D.Float ( 50.0f, 0.0f, veeX - stroke.getLineWidth() * 0.25f, 0.0f ) );
        }
    }

    static class CurvedArrow  implements Arrow
    {
        // to draw a nice curved arrow, fill a V shape rather than stroking it with lines
        public void draw ( Graphics2D g )
        {
            // as we're filling rather than stroking, control point is at the apex,

            float arrowRatio = 0.5f;
            float arrowLength = 80.0f;

            BasicStroke stroke = ( BasicStroke ) g.getStroke();

            float endX = 350.0f;

            float veeX = endX - stroke.getLineWidth() * 0.5f / arrowRatio;

            // vee
            Path2D.Float path = new Path2D.Float();

            float waisting = 0.5f;

            float waistX = endX - arrowLength * 0.5f;
            float waistY = arrowRatio * arrowLength * 0.5f * waisting;
            float arrowWidth = arrowRatio * arrowLength;

            path.moveTo ( veeX - arrowLength, -arrowWidth );
            path.quadTo ( waistX, -waistY, endX, 0.0f );
            path.quadTo ( waistX, waistY, veeX - arrowLength, arrowWidth );

            // end of arrow is pinched in
            path.lineTo ( veeX - arrowLength * 0.75f, 0.0f );
            path.lineTo ( veeX - arrowLength, -arrowWidth );

            g.setColor ( Color.BLUE );
            g.fill ( path );

            // move stem back a bit
            g.setColor ( Color.RED );
            g.draw ( new Line2D.Float ( 50.0f, 0.0f, veeX - arrowLength * 0.5f, 0.0f ) );
        }
    }
}
person Pete Kirkham    schedule 08.01.2010
comment
Я не понимаю, как ты это используешь. - person Stefan Reich; 15.03.2017

Хотя статья Пита очень всеобъемлющая, я использую этот метод, чтобы нарисовать очень простую линию с маленьким треугольником на конце.

// create an AffineTransform 
// and a triangle centered on (0,0) and pointing downward
// somewhere outside Swing's paint loop
AffineTransform tx = new AffineTransform();
Line2D.Double line = new Line2D.Double(0,0,100,100);

Polygon arrowHead = new Polygon();  
arrowHead.addPoint( 0,5);
arrowHead.addPoint( -5, -5);
arrowHead.addPoint( 5,-5);

// [...]
private void drawArrowHead(Graphics2D g2d) {  
    tx.setToIdentity();
    double angle = Math.atan2(line.y2-line.y1, line.x2-line.x1);
    tx.translate(line.x2, line.y2);
    tx.rotate((angle-Math.PI/2d));  

    Graphics2D g = (Graphics2D) g2d.create();
    g.setTransform(tx);   
    g.fill(arrowHead);
    g.dispose();
}
person Vicente Reig    schedule 22.06.2010
comment
Это отлично сработало для меня. Большое спасибо за более простое решение! - person Brain; 23.01.2017

Это мой подход, только абсолютная математика:

/**
 * Draw an arrow line between two points.
 * @param g the graphics component.
 * @param x1 x-position of first point.
 * @param y1 y-position of first point.
 * @param x2 x-position of second point.
 * @param y2 y-position of second point.
 * @param d  the width of the arrow.
 * @param h  the height of the arrow.
 */
private void drawArrowLine(Graphics g, int x1, int y1, int x2, int y2, int d, int h) {
    int dx = x2 - x1, dy = y2 - y1;
    double D = Math.sqrt(dx*dx + dy*dy);
    double xm = D - d, xn = xm, ym = h, yn = -h, x;
    double sin = dy / D, cos = dx / D;

    x = xm*cos - ym*sin + x1;
    ym = xm*sin + ym*cos + y1;
    xm = x;

    x = xn*cos - yn*sin + x1;
    yn = xn*sin + yn*cos + y1;
    xn = x;

    int[] xpoints = {x2, (int) xm, (int) xn};
    int[] ypoints = {y2, (int) ym, (int) yn};

    g.drawLine(x1, y1, x2, y2);
    g.fillPolygon(xpoints, ypoints, 3);
}
person phibao37    schedule 13.12.2014

В прошлом я написал следующий метод для создания формы стрелки, которую затем можно заполнить ((Graphics2D) g).fill(shape);

public static Shape createArrowShape(Point fromPt, Point toPt) {
    Polygon arrowPolygon = new Polygon();
    arrowPolygon.addPoint(-6,1);
    arrowPolygon.addPoint(3,1);
    arrowPolygon.addPoint(3,3);
    arrowPolygon.addPoint(6,0);
    arrowPolygon.addPoint(3,-3);
    arrowPolygon.addPoint(3,-1);
    arrowPolygon.addPoint(-6,-1);


    Point midPoint = midpoint(fromPt, toPt);

    double rotate = Math.atan2(toPt.y - fromPt.y, toPt.x - fromPt.x);

    AffineTransform transform = new AffineTransform();
    transform.translate(midPoint.x, midPoint.y);
    double ptDistance = fromPt.distance(toPt);
    double scale = ptDistance / 12.0; // 12 because it's the length of the arrow polygon.
    transform.scale(scale, scale);
    transform.rotate(rotate);

    return transform.createTransformedShape(arrowPolygon);
}

private static Point midpoint(Point p1, Point p2) {
    return new Point((int)((p1.x + p2.x)/2.0), 
                     (int)((p1.y + p2.y)/2.0));
}
person Michael Zlatkovsky - Microsoft    schedule 26.03.2011

На всякий случай, если вам нужна непрограммная стрелка (т.е. для текста) быстрым способом, вы можете использовать код <html> для создания стрелки как текст, просто поместите свой HTML-код в метод .setText() для компонента. У меня java 1.8u202 работает нормально.

myLabel.setText("<html><body>&#8592;</body></html>");

этот код &#8592; предназначен для стрелки, указывающей влево

другие направления стрелок HTML-код с этого веб-сайта

person mhamad arsalan    schedule 19.02.2019
comment
крутая идея! и вы можете стилизовать их и т.д.:‹div style=float:left;font-size: 60px;font-weight: 600;margin:20px;›→‹/div› ‹div style=float:left;font-size: 60px ;размер шрифта: 600;поле:20px;› ←‹/div› - person user2677034; 25.06.2020