繁体   English   中英

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

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

我对 Android 还很陌生,并且一直在玩弄 Canvas。 我正在尝试画一个箭头,但我只是很幸运地画了轴,没有一个箭头在工作。

我搜索了一下,发现了一个 Java 示例,但是 Android 没有GeneralPathAffineTransform

现在我的代码如下所示(箭头看起来不像箭头):

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

我查看了以下线程以获得指导:
* http://www.java-forums.org/awt-swing/6241-how-u-rotate-arrow-mark-line-moves-accordingly.html
* 如何在 Java 中绘制有向箭头线?

使用“Path myPath = new Path();”怎么样? 您将在其中给出 x 和 y 位置以使用线条创建三角形并填充它。 您可以阅读它,这是我从某个地方获取的示例。

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

我的箭头绘图代码,也许对某人有用:

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

我尝试了这段代码,它一直运行良好:

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

我一直有同样的问题,我需要一个箭头指向某个方向。 在玩弄了绘图算法之后,我决定最简单的方法是使用 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);

那么你的 bitmap 可以是你喜欢的任何花哨的箭头。

如果您正在寻找在一秒钟内用固定长度的头线绘制数千个箭头的解决方案,请试试这个 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);
}

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

这是非常适合我在 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;
    }
}

使用如下路径并相应地调整坐标:

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

从这个答案复制过去 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);
}

这是我的箭头绘制代码,没有显式使用三角函数(尽管基础数学显然使用三角函数)数学使箭头头像一个正方形的一半(对角线切割),其中变量 L 是对角线的长度。 此外,箭头在点 p2 结束,这意味着对于 p2 和 p1 之间的微小差异,箭头将在 p2 之前绘制足够的 L。此外,如果 p1 和 p2 相同,则不会绘制箭头,因为数学会导致除以零。 我建议你使用 Paint.Style.FILL_AND_STROKE 来绘制这个箭头。 我愿意接受任何问题。

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