【问题标题】:How do I draw an arrowhead (in Android)?如何绘制箭头(在 Android 中)?
【发布时间】:2011-07-15 22:18:49
【问题描述】:

我对 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
* How to draw a directed arrow line in Java?

【问题讨论】:

    标签: java android draw shape


    【解决方案1】:

    如何使用“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);
    

    【讨论】:

    • 啊哈! (在我没有发布的尝试中)我忘记使用moveTo 方法让它知道从哪里开始绘图!我假设它创建了一个看起来很奇怪的对象,因为它一定是从 (0,0) 绘制的。感谢您的帮助!
    【解决方案2】:

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

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

    【讨论】:

    • 哥们很抱歉,但您的代码不会生成箭头
    • @AyushBansal,它有效。只需将半径和角度更改为半径 = 30f 角度 = 35f
    • 这个答案很好
    【解决方案3】:

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

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

    【讨论】:

    • 非常好的解决方案。谢谢。我对其进行了修改以提供与缩放级别/线长无关的固定大小的箭头。所需要的只是替换“float frac = (float) 0.1;” int ARROWHEAD_LENGTH=20; float sideZ= (float) Math.sqrt(deltaX deltaX + deltaYdeltaY); //Z=斜边 float frac = ARROWHEAD_LENGTH
    【解决方案4】:

    我也遇到了同样的问题,我需要一个箭头指向某个方向。在玩弄了绘图算法之后,我决定最简单的方法是使用位图并简单地使用矩阵来旋转它,例如

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

    那么你的位图可以是你喜欢的任何漂亮的箭头。

    【讨论】:

    • 感谢您的回复和洞察!我一直在努力避免在其中放置图像(因为我在网上找到了如何使用图像进行操作的示例)。但是,我可能需要使用图像。如果我确实需要使用该图像,我会回到这里并给你的代码一个镜头。 +1 示例:)
    【解决方案5】:

    如果您正在寻找在一秒钟内绘制数千个箭头的解决方案,并且具有固定长度的头部线,请尝试此功能(仅绘制箭头):

    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

    【讨论】:

      【解决方案6】:

      这里的代码非常适合我在画布上画线时绘制箭头

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

      【讨论】:

        【解决方案7】:

        如下使用Path 并相应调整坐标:

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

        【讨论】:

          【解决方案8】:

          复制此答案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);
          }
          

          【讨论】:

            【解决方案9】:

            这是我没有显式使用三角函数的箭头绘制代码(尽管基础数学显然使用三角函数) 数学使箭头看起来像一个正方形的一半(对角线切割),其中变量 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);
                }
            

            【讨论】:

              猜你喜欢
              • 2012-11-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多