简体   繁体   English

如何绘制箭头(在 Android 中)?

[英]How do I draw an arrowhead (in Android)?

I'm fairly new to Android and have been toying around with Canvas.我对 Android 还很陌生,并且一直在玩弄 Canvas。 I'm attempting to draw an arrow but I'm only having luck with drawing the shaft, none of the arrowhead is working.我正在尝试画一个箭头,但我只是很幸运地画了轴,没有一个箭头在工作。

I have searched a bit and found a Java example, but Android doesn't have GeneralPath or AffineTransform .我搜索了一下,发现了一个 Java 示例,但是 Android 没有GeneralPathAffineTransform

Right now my code looks like the following (the arrowhead looks nothing like an arrowhead):现在我的代码如下所示(箭头看起来不像箭头):

public class DrawableView extends View {
    Context mContext;
    private int centerX;
    private int centerY;
    private int radius;
    private double arrLength;
    private double arrHeading;
    private int margin = 10;

    public DrawableView(Context context) {
        super(context);
        mContext = context;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        //Paint Background
        Paint background = new Paint();
        background.setColor(getResources().getColor(R.color.background);
        canvas.drawRect(0, 0, getWidth(), getHeight(), background);

        //Set vars for Arrow Paint
        Paint paint = new Paint();
        paint.setColor(getResources().getColor(R.color.arrowColor);
        centerX = getWidth() / 2;
        centerY = getHeight() / 2;
        arrLength = radius - 10;

        if(centerX < centerY)
            radius = centerX - margin;
        else 
            radius = centerY - margin;

        //Draw Shaft
        int[] xy = findArrowPos(arrLength, arrHeading);
        canvas.drawLine(centerX, centerY, xy[0], xy[1], paint);

        //Draw ArrowHead
            //This is where I'm confused

    }

    private int[] findArrowPos(double length, double angle) {
        int[] points = new int[2];
        double theta = Math.toRadians(angle);
        points[0] = centerX + (int) (length * Math.cos(theta));
        points[1] = centerY + (int) (length * Math.sin(theta));
        return points;
    }
}

I have taken a look at the following threads for guidance:我查看了以下线程以获得指导:
* http://www.java-forums.org/awt-swing/6241-how-u-rotate-arrow-mark-line-moves-accordingly.html * http://www.java-forums.org/awt-swing/6241-how-u-rotate-arrow-mark-line-moves-accordingly.html
* How to draw a directed arrow line in Java? * 如何在 Java 中绘制有向箭头线?

How about using "Path myPath = new Path();"使用“Path myPath = new Path();”怎么样? where you would give the x and y positions to create a triangle using lines and filling it.您将在其中给出 x 和 y 位置以使用线条创建三角形并填充它。 You can read about it, here is an example I took from somewhere.您可以阅读它,这是我从某个地方获取的示例。

// create and draw triangles
// use a Path object to store the 3 line segments
// use .offset to draw in many locations
// note: this triangle is not centered at 0,0
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
paint.setColor(Color.RED);
Path path = new Path();
path.moveTo(0, -10);
path.lineTo(5, 0);
path.lineTo(-5, 0);
path.close();
path.offset(10, 40);
canvas.drawPath(path, paint);
path.offset(50, 100);
canvas.drawPath(path, paint);
// offset is cumlative
// next draw displaces 50,100 from previous
path.offset(50, 100);
canvas.drawPath(path, paint);

My Arrow Drawing code, maybe it can be of some use for somebody:我的箭头绘图代码,也许对某人有用:

    /**
 * Draw an arrow
 * change internal radius and angle to change appearance
 * - angle : angle in degrees of the arrows legs
 * - radius : length of the arrows legs
 * @author Steven Roelants 2017
 *
 * @param paint
 * @param canvas
 * @param from_x
 * @param from_y
 * @param to_x
 * @param to_y
 */
private void drawArrow(Paint paint, Canvas canvas, float from_x, float from_y, float to_x, float to_y)
{
    float angle,anglerad, radius, lineangle;

    //values to change for other appearance *CHANGE THESE FOR OTHER SIZE ARROWHEADS*
    radius=10;
    angle=15;

    //some angle calculations
    anglerad= (float) (PI*angle/180.0f);
    lineangle= (float) (atan2(to_y-from_y,to_x-from_x));

    //tha line
    canvas.drawLine(from_x,from_y,to_x,to_y,paint);

    //tha triangle
    Path path = new Path();
    path.setFillType(Path.FillType.EVEN_ODD);
    path.moveTo(to_x, to_y);
    path.lineTo((float)(to_x-radius*cos(lineangle - (anglerad / 2.0))),
            (float)(to_y-radius*sin(lineangle - (anglerad / 2.0))));
    path.lineTo((float)(to_x-radius*cos(lineangle + (anglerad / 2.0))),
            (float)(to_y-radius*sin(lineangle + (anglerad / 2.0))));
    path.close();

    canvas.drawPath(path, paint);
}

I try this code it has been working perfectly:我尝试了这段代码,它一直运行良好:

switch (event.getAction())
{
   case MotionEvent.ACTION_DOWN:
        mPath.reset();
        mPath.moveTo(x, y);
        mX = x;
        mY = y;
        startPoint = new PointF(event.getX(), event.getY());
        endPoint = new PointF();
        invalidate();
        break;
    case MotionEvent.ACTION_MOVE:
            float dx = Math.abs(x - mX);
        System.out.println("action move");
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE)
        {
        //  currentDrawingPath.path.quadTo(mX,mY,(x + mX)/2, (y + mY)/2);
        }
        mX = x;
        mY = y;
          endPoint.x = event.getX();
          endPoint.y = event.getY();
          isDrawing = true;
          invalidate();
        break;
    case MotionEvent.ACTION_UP:
           mPath.lineTo(mX, mY);
           float deltaX =   endPoint.x-startPoint.x;
           float deltaY =   endPoint.y-startPoint.y;
           float frac = (float) 0.1;
     float point_x_1 = startPoint.x + (float) ((1 - frac) * deltaX + frac * deltaY);
     float point_y_1 = startPoint.y + (float) ((1 - frac) * deltaY - frac * deltaX);
           float point_x_2 = endPoint.x;
           float point_y_2 = endPoint.y;
     float point_x_3 = startPoint.x + (float) ((1 - frac) * deltaX - frac * deltaY);
     float point_y_3 = startPoint.y + (float) ((1 - frac) * deltaY + frac * deltaX);
           mPath.moveTo(point_x_1, point_y_1);
           mPath.lineTo(point_x_2, point_y_2);
           mPath.lineTo(point_x_3, point_y_3);
           mPath.lineTo(point_x_1, point_y_1);
           mPath.lineTo(point_x_1, point_y_1);
            mCanvas.drawPath(mPath, ppaint);
            endPoint.x = event.getX();
            endPoint.y = event.getY();
            isDrawing = false;
            invalidate();
        break;
    default:
        break;
}       

I've been having the same problem, I need an arrow to point in a certain direction.我一直有同样的问题,我需要一个箭头指向某个方向。 After playing around with drawing algorithms I decided the simplest method is to use a bitmap & simply use a Matrix to rotate it, eg在玩弄了绘图算法之后,我决定最简单的方法是使用 bitmap 并简单地使用矩阵来旋转它,例如

ImageView image = (ImageView) findViewById(R.id.bitmap_image);
Bitmap bMap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
Matrix mat = new Matrix();
mat.postRotate(90);
Bitmap bMapRotate = Bitmap.createBitmap(bMap, 0, 0, bMap.getWidth(), bMap.getHeight(), mat, true);
image.setImageBitmap(bMapRotate);

then your bitmap can be any fancy looking arrow you like.那么你的 bitmap 可以是你喜欢的任何花哨的箭头。

If you are looking for the solution to draw thousands of arrows under a second, with fixed length head lines, try this function (draws only arrow heads):如果您正在寻找在一秒钟内用固定长度的头线绘制数千个箭头的解决方案,请试试这个 function(仅绘制箭头):

private void fillArrow(Paint paint, Canvas canvas, float x0, float y0, float x1, float y1) {
    paint.setStyle(Paint.Style.STROKE);

    int arrowHeadLenght = 10;
    int arrowHeadAngle = 45;
    float[] linePts = new float[] {x1 - arrowHeadLenght, y1, x1, y1};
    float[] linePts2 = new float[] {x1, y1, x1, y1 + arrowHeadLenght};
    Matrix rotateMat = new Matrix();

    //get the center of the line
    float centerX = x1;
    float centerY = y1;

    //set the angle
    double angle = Math.atan2(y1 - y0, x1 - x0) * 180 / Math.PI + arrowHeadAngle;

    //rotate the matrix around the center
    rotateMat.setRotate((float) angle, centerX, centerY);
    rotateMat.mapPoints(linePts);
    rotateMat.mapPoints(linePts2);

    canvas.drawLine(linePts [0], linePts [1], linePts [2], linePts [3], paint);
    canvas.drawLine(linePts2 [0], linePts2 [1], linePts2 [2], linePts2 [3], paint);
}

Based on https://gamedev.stackexchange.com/questions/44456/drawing-lines-on-android-with-matrix基于https://gamedev.stackexchange.com/questions/44456/drawing-lines-on-android-with-matrix

Here is code working perfect for me draw arrow head while drawing line on canvas这是非常适合我在 canvas 上画线时绘制箭头的代码

package com.example.canvasexample;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;

import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;

public class DrawerViewArrow extends View {
    private ArrayList<Path> drawingLinePath;
    private ArrayList<Path> drawingArrowPath;
    private ArrayList<Paint> drawingLinePaint;
    private int pathIndex = 0;
    private float startX = -1, startY = -1;
    private float mX = -1, mY = -1;

    public int arrowLength = 80;
    public int arrowWidth = 45;
    public int strokeWidth = 10;

    public DrawerViewArrow(Context context) {
        super(context);
        initPath();
    }

    public DrawerViewArrow(Context context, @NonNull AttributeSet attrs) {
        super(context, attrs);
        initPath();
    }

    public DrawerViewArrow(Context context, @NonNull AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPath();
    }

    private Paint initPaint() {
        Paint mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(Color.GREEN);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(strokeWidth);
        return mPaint;
    }

    private void initPath() {
        drawingLinePath = new ArrayList<>();
        drawingArrowPath = new ArrayList<>();
        drawingLinePath.add(new Path());
        drawingArrowPath.add(new Path());
        drawingLinePaint = new ArrayList<>();
        drawingLinePaint.add(initPaint());
        pathIndex++;
    }

    private Path createPath(MotionEvent event) {
        Path path = new Path();
        path.moveTo(event.getX(), event.getY());
        return path;
    }

    private void updateIndex(MotionEvent event) {
        if (pathIndex == drawingLinePath.size()) {
            drawingLinePath.add(createPath(event));
            drawingArrowPath.add(createPath(event));
            drawingLinePaint.add(initPaint());
            pathIndex++;
        }
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (startX > -1 && mX > -1) {
            canvas.drawLine(startX, startY, mX, mY, initPaint());
            drawArrow(canvas);
        }

        for (int index = 0; index < pathIndex; index++) {
            Path path = drawingLinePath.get(index);
            Path arrow_path = drawingArrowPath.get(index);
            Paint paint = drawingLinePaint.get(index);
            canvas.drawPath(path, paint);
            canvas.drawPath(arrow_path, paint);
        }
    }

    private void drawArrow(Canvas canvas) {
        double angle = calculateAngle(startX, startY, mX, mY);

        float final_angle = (float) (180 - angle);

        Path arrow_path = new Path();

        Matrix arrow_matrix = new Matrix();

        arrow_matrix.postRotate(final_angle, mX, mY);

        arrow_path.moveTo(mX, mY);
        arrow_path.lineTo(mX - arrowWidth, mY + arrowLength);
        arrow_path.moveTo(mX, mY);
        arrow_path.lineTo(mX + arrowWidth, mY + arrowLength);
        arrow_path.lineTo(mX - (arrowWidth), mY + arrowLength);
        arrow_path.transform(arrow_matrix);

        canvas.drawPath(arrow_path, initPaint());
    }

    private void saveArrow() {
        if (mX == -1 || mY == -1) {
            return;
        }

        double angle = calculateAngle(startX, startY, mX, mY);

        float final_angle = (float) (180 - angle);

        Path arrow_path = drawingArrowPath.get(pathIndex - 1);

        Matrix arrow_matrix = new Matrix();

        arrow_matrix.postRotate(final_angle, mX, mY);

        arrow_path.moveTo(mX, mY);
        arrow_path.lineTo(mX - arrowWidth, mY + arrowLength);
        arrow_path.moveTo(mX, mY);
        arrow_path.lineTo(mX + arrowWidth, mY + arrowLength);
        arrow_path.lineTo(mX - (arrowWidth), mY + arrowLength);
        arrow_path.transform(arrow_matrix);
    }

    public double calculateAngle(double x1, double y1, double x2, double y2) {
        double angle = Math.toDegrees(Math.atan2(x2 - x1, y2 - y1));

        angle = angle + Math.ceil(-angle / 360) * 360; //Keep angle between 0 and 360

        return angle;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case ACTION_UP:
                actionUp(event);
                break;
            case ACTION_MOVE:
                actionMove(event);
                break;
            case ACTION_DOWN:
                actionDown(event);
                break;
        }
        invalidate();
        return true;
    }

    private void actionDown(MotionEvent event) {
        updateIndex(event);
        startX = event.getX();
        startY = event.getY();
    }

    private void actionMove(MotionEvent event) {
        mX = event.getX();
        mY = event.getY();
    }

    private void actionUp(MotionEvent event) {
        drawingLinePath.get(pathIndex - 1).lineTo(event.getX(), event.getY());
        saveArrow();
        startX = -1;
        startY = -1;
        mX = -1;
        mY = -1;
    }
}

Use a Path as below and adjust the co-ordinates accordingly:使用如下路径并相应地调整坐标:

// Construct a wedge-shaped path
Path mPath = new Path();
mPath.moveTo(0, -50);
mPath.lineTo(-20, 60);
mPath.lineTo(0, 50);
mPath.lineTo(20, 60);
mPath.close();

Copypast from this answer https://stackoverflow.com/a/29383352/9975029从这个答案复制过去 https://stackoverflow.com/a/29383352/9975029

private void fillArrow(Canvas canvas, float x0, float y0, float x1, float y1) {
    paint.setStyle(Paint.Style.FILL);

    float deltaX = x1 - x0;
    float deltaY = y1 - y0;
    double distance = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY));
    float frac = (float) (1 / (distance / 30));

    float point_x_1 = x0 + (float) ((1 - frac) * deltaX + frac * deltaY);
    float point_y_1 = y0 + (float) ((1 - frac) * deltaY - frac * deltaX);

    float point_x_2 = x1;
    float point_y_2 = y1;

    float point_x_3 = x0 + (float) ((1 - frac) * deltaX - frac * deltaY);
    float point_y_3 = y0 + (float) ((1 - frac) * deltaY + frac * deltaX);

    Path path = new Path();
    path.setFillType(Path.FillType.EVEN_ODD);

    path.moveTo(point_x_1, point_y_1);
    path.lineTo(point_x_2, point_y_2);
    path.lineTo(point_x_3, point_y_3);
    path.lineTo(point_x_1, point_y_1);
    path.lineTo(point_x_1, point_y_1);
    path.close();

    canvas.drawPath(path, paint);
}

Here's my arrow drawing code without using trig functions explicitly (although the underlying math obviously uses trig) The math makes an arrow head like half of a square (cut diagonally) where variable L is the length of the diagonal.这是我的箭头绘制代码,没有显式使用三角函数(尽管基础数学显然使用三角函数)数学使箭头头像一个正方形的一半(对角线切割),其中变量 L 是对角线的长度。 Also, the arrow ends at point p2 which means that for small difference between p2 and p1, the arrow head will be drawn 'before' p2 for sufficient L. Also if p1 and p2 are the same, the arrow will not be drawn because the math would cause division by zero.此外,箭头在点 p2 结束,这意味着对于 p2 和 p1 之间的微小差异,箭头将在 p2 之前绘制足够的 L。此外,如果 p1 和 p2 相同,则不会绘制箭头,因为数学会导致除以零。 I suggest you use Paint.Style.FILL_AND_STROKE to draw this arrow.我建议你使用 Paint.Style.FILL_AND_STROKE 来绘制这个箭头。 I'm open for any questions.我愿意接受任何问题。

void drawArrow(Canvas canvas, Point p1, Point p2, float L) {
        float fsin, fcos;
        double d;

        if(p1.equals(p2))
            return;

        d = Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
        fsin = (p2.y - p1.y)/(float)d;
        fcos = (p2.x - p1.x)/(float)d;
        PointF p3 = new PointF(p2.x - L/2*(fsin + fcos), p2.y + L/2*(fcos - fsin));
        PointF p4 = new PointF(p2.x + L/2*(fsin - fcos), p2.y - L/2*(fsin + fcos));

        canvas.drawLine(p1.x, p1.y, p2.x, p2.y, arrowPaint);

        Path path = new Path();
        path.setFillType(Path.FillType.EVEN_ODD);
        path.moveTo(p2.x, p2.y);
        path.lineTo(p3.x, p3.y);
        path.lineTo(p4.x, p4.y);
        path.close();

        canvas.drawPath(path, arrowPaint);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM