Search code examples
androidmathdraw

How to draw an arrowhead in a free handed drawn line in Android?


I need to make a arrow head in a line that is defined by a lot of points, that the user will drawn.

I hope that I could be clear about what is my question.

Thanks.

Note: All the questions/answers that I saw here were to resolve the problem of a line that was defined by two points, taken in the ACTION_DOWN and ACTION_UP. It's different in my case, because I need to take the first point while the line is being drawn.

This is my onTouch() method. It just draw a line defined by where the user touches in the screen.

public boolean onTouch(View v, MotionEvent event) { 
    if(getEditMode()) { 

        float eventX = event.getX();
        float eventY = event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                path.moveTo(eventX, eventY); 

                break;
            case MotionEvent.ACTION_MOVE:
                path.lineTo(eventX, eventY); 

                break;
            case MotionEvent.ACTION_UP:
                             //Here i have to draw an arrow.

                drawCanvas.drawPath(path, paint);
                path.reset();

                invalidate();

                break;

        }

        invalidate();
        return true; 

    } else {
        return false; 

    }
}

Solution

  • So, I found a solution.

    I store all the points that construct the line in an arraylist.

    After I took a point of the line that would be in the 0.9 * arraylist.size() as reference to draw an arrowhead that follow the line direction.

    Here is my onTouch() method and the funciont that I used to draw the arrow.

    public boolean onTouch(View v, MotionEvent event) {
        if(getEditMode()) {
            float eventX = event.getX();
            float eventY = event.getY();
    
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN: {
                    path.moveTo(eventX, eventY);
    
                    pList.clear(); // When I start a line need to clear the previous coorfinates.
    
                    break;
    
                }
    
                case MotionEvent.ACTION_MOVE: {
                    int hSize = event.getHistorySize();
    
                    path.lineTo(eventX, eventY);
    
                    PointF aux, auxH;
    
                    if(darrow) {
                        if(hSize > 0) { // If movement is too fast.
                            for(int i = 0; i < hSize; i++) {
                                auxH = new PointF(event.getHistoricalX(i), event.getHistoricalY(i));
    
                                pList.add(auxH);
    
                            }
    
                        }
    
                        aux = new PointF(eventX, eventY);
    
                        pList.add(aux);
    
                    }
    
                    break;
    
                }   
    
                case MotionEvent.ACTION_UP: {
                    pend.x = eventX;
                    pend.y = eventY;
    
                    if(darrow) { // If need to draw an arrow head to the end of the line;
                        arrowPath = drawArrow(pend); // Store the right arrowhead to a path.
    
                    }
    
                    drawCanvas.drawPath(path, paint);
                    drawCanvas.drawPath(arrowPath, paint);
    
                    path.reset();
                    arrowPath.reset();
    
                    invalidate();
    
                    break;
    
                }
    
            }
    
            invalidate();
    
            return true;
    
        } else {
            return false;
    
        }
    
    }
    

    And the drawArrow(PointF) function:

    private Path drawArrow(PointF pfinal) {
        float dx, dy;
        PointF p1, p2;
        PointF pstart;
        Path auxPath = new Path();
    
        if(pList.size() > 0) {
            PointF[] auxArray = pList.toArray(new PointF[pList.size()]);
    
            int index = (int)(auxArray.length * 0.9);
    
            Log.d(msg, "Size: " + auxArray.length + " | index: " + index);
    
            pstart = auxArray[index];
    
            dx = pfinal.x - pstart.x;
            dy = pfinal.y - pstart.y;
    
            float length = (float)Math.sqrt(dx * dx + dy * dy);
    
            float unitDx = dx / length;
            float unitDy = dy / length;
    
            final int arrowSize = 10;
    
            p1 = new PointF(
                    (float)(pfinal.x - unitDx * arrowSize - unitDy * arrowSize),
                    (float)(pfinal.y - unitDy * arrowSize + unitDx * arrowSize));
    
            p2 = new PointF(
                    (float)(pfinal.x - unitDx * arrowSize + unitDy * arrowSize),
                    (float)(pfinal.y - unitDy * arrowSize - unitDx * arrowSize));
    
            auxPath.moveTo(pfinal.x, pfinal.y);
            auxPath.lineTo(p1.x, p1.y);
            auxPath.moveTo(pfinal.x, pfinal.y);
            auxPath.lineTo(p2.x, p2.y);
    
            auxPath.close();
    
        }
    
        return auxPath;
    
    }