Android Question Draw an arrow using Canvas

dieterp

Active Member
Licensed User
Longtime User
How do I draw an arrow using the canvas object? The user will use his finger to drag a line on the device, and when they lift their finger it must then automatically draw the arrow head.

I've got the fundamentals in place to draw the line on the canvas as you drag your finger, but I'm not certain about the trigonometry required to draw the arrow head
 
Solution
You need to show your code.
Do you declare the Panel as a B4XView ?
How do declare the B4XCanvas ?
Maybe you are missing an Invalidate method to update the canvas.

Attached a small B4XPages project tested with B4A and B4J.

klaus

Expert
Licensed User
Longtime User
Here you are, the routine draws an arrow at the end point.

B4X:
'xb, yb coordinates begin point
'xe, ye coordinates end point
Private Sub DrawArrow(cvs As B4XCanvas, xb As Int, yb As Int, xe As Int, ye As Int, ArrowLength As Int, HalfArrowWidth As Int, Color As Int)
    Private Path As B4XPath
    
    Path.Initialize(xe, ye)
    Select True
        Case xe = xb And ye > yb    'down
            Path.LineTo(xe - HalfArrowWidth, ye - ArrowLength)
            Path.LineTo(xe + HalfArrowWidth, ye - ArrowLength)
        Case xe = xb And ye < yb    'up
            Path.LineTo(xe - HalfArrowWidth, ye + ArrowLength)
            Path.LineTo(xe + HalfArrowWidth, ye + ArrowLength)
        Case xe < xb And ye = yb    'left
            Path.LineTo(xe + ArrowLength, ye + HalfArrowWidth)
            Path.LineTo(xe + ArrowLength, ye - HalfArrowWidth)
        Case xe > xb And ye = yb    'right
            Path.LineTo(xe - ArrowLength, ye + HalfArrowWidth)
            Path.LineTo(xe - ArrowLength, ye - HalfArrowWidth)
        Case Else
            Private Alpha As Double
            Alpha = ATan2((ye - yb) , (xe - xb))
            Path.LineTo(xe - Cos(Alpha) * ArrowLength + Sin(Alpha) * HalfArrowWidth, ye - Sin(Alpha) * ArrowLength - Cos(Alpha) * HalfArrowWidth)
            Path.LineTo(xe - Cos(Alpha) * ArrowLength - Sin(Alpha) * HalfArrowWidth, ye - Sin(Alpha) * ArrowLength + Cos(Alpha) * HalfArrowWidth)
    End Select
    cvs.DrawPath(Path, Color, True, 3dip)
End Sub
 
Upvote 0

MasterGy

Member
Licensed User
Hogyan rajzolhatok nyilat a vászon objektum segítségével? A felhasználó az ujjával húz egy vonalat az eszközön, és amikor felemeli az ujját, automatikusan meg kell rajzolnia a nyílfejet.

Megvannak az alapok ahhoz, hogy meghúzhassam a vonalat a vásznon, miközben húzza az ujját, de nem vagyok biztos a nyílfej rajzolásához szükséges trigonometriában.
if the line x1y1x2y2 , then the line's angle : atan2(x2-x1, y2-y1) in radian. If you need degree : degree = radian / (3.14/180)
 
Upvote 0

dieterp

Active Member
Licensed User
Longtime User
Here you are, the routine draws an arrow at the end point.

B4X:
'xb, yb coordinates begin point
'xe, ye coordinates end point
Private Sub DrawArrow(cvs As B4XCanvas, xb As Int, yb As Int, xe As Int, ye As Int, ArrowLength As Int, HalfArrowWidth As Int, Color As Int)
    Private Path As B4XPath
   
    Path.Initialize(xe, ye)
    Select True
        Case xe = xb And ye > yb    'down
            Path.LineTo(xe - HalfArrowWidth, ye - ArrowLength)
            Path.LineTo(xe + HalfArrowWidth, ye - ArrowLength)
        Case xe = xb And ye < yb    'up
            Path.LineTo(xe - HalfArrowWidth, ye + ArrowLength)
            Path.LineTo(xe + HalfArrowWidth, ye + ArrowLength)
        Case xe < xb And ye = yb    'left
            Path.LineTo(xe + ArrowLength, ye + HalfArrowWidth)
            Path.LineTo(xe + ArrowLength, ye - HalfArrowWidth)
        Case xe > xb And ye = yb    'right
            Path.LineTo(xe - ArrowLength, ye + HalfArrowWidth)
            Path.LineTo(xe - ArrowLength, ye - HalfArrowWidth)
        Case Else
            Private Alpha As Double
            Alpha = ATan2((ye - yb) , (xe - xb))
            Path.LineTo(xe - Cos(Alpha) * ArrowLength + Sin(Alpha) * HalfArrowWidth, ye - Sin(Alpha) * ArrowLength - Cos(Alpha) * HalfArrowWidth)
            Path.LineTo(xe - Cos(Alpha) * ArrowLength - Sin(Alpha) * HalfArrowWidth, ye - Sin(Alpha) * ArrowLength + Cos(Alpha) * HalfArrowWidth)
    End Select
    cvs.DrawPath(Path, Color, True, 3dip)
End Sub

Is there a solution that uses just the native Canvas object and not B4XCanvas version? I also use DrawOval and I see the B4XCanvas doesn't have that
 
Upvote 0

dieterp

Active Member
Licensed User
Longtime User
Are B4XCanvas and B4Path standalone libraries, or do they fall under the internal B4XDrawer library?
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
And a more general routine with the angle.

B4X:
'xp, yp coordinates of the arrowhead
'Angle of the direction towards the arrowhead
Private Sub DrawArrow(cvs As B4XCanvas, xp As Int, yp As Int, Angle As Double, ArrowLength As Int, HalfArrowWidth As Int, Color As Int)
    Private Path As B4XPath
    
    Path.Initialize(xp, yp)
    Path.LineTo(xp - Cos(Angle) * ArrowLength + Sin(Angle) * HalfArrowWidth, yp - Sin(Angle) * ArrowLength - Cos(Angle) * HalfArrowWidth)
    Path.LineTo(xp - Cos(Angle) * ArrowLength - Sin(Angle) * HalfArrowWidth, yp - Sin(Angle) * ArrowLength + Cos(Angle) * HalfArrowWidth)
    cvs.DrawPath(Path, Color, True, 3dip)
End Sub

And a routine drawing a line with Arrows showing the calculation of the Angle.
B4X:
Private Sub DrawLineWithArrows(cvs As B4XCanvas, xb, yb, xe, ye, ArrowLength As Int, HalfArrowWidth As Int, Color As Int, BeginArrow As Boolean, EndArrow As Boolean)
    Private Angle As Double
    
    cvs.DrawLine(xb, yb, xe, ye, Color, 1dip)
    If BeginArrow Then
        Angle = ATan2(yb - ye, xb - xe)
        DrawArrow(cvs, xb, yb, Angle, ArrowLength, HalfArrowWidth, Color)
    End If
    If EndArrow Then
        Angle = ATan2(ye - yb, xe - xb)
        DrawArrow(cvs, xe, ye, Angle, ArrowLength, HalfArrowWidth, Color)       
    End If
End Sub

These routine do not look good with thicker lines.
 
Upvote 0

dieterp

Active Member
Licensed User
Longtime User
I have selected the XUI Library, and am now using B4XCanvas, B4Rect and B4Path to draw Rectangles and Ovals on the canvas. That works nicely, but I cannot get the arrow shapes to appear. What step could I be missing out on?
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
You need to show your code.
Do you declare the Panel as a B4XView ?
How do declare the B4XCanvas ?
Maybe you are missing an Invalidate method to update the canvas.

Attached a small B4XPages project tested with B4A and B4J.
 

Attachments

  • DrawArrow.zip
    14.4 KB · Views: 82
Last edited:
Upvote 1
Solution

dieterp

Active Member
Licensed User
Longtime User
Thanks for the demo project. I've managed to get it working now. There were a few things I changed, but the most likely reason was that the panel was not declared as a B4XView
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
The big advantage of using the XUI library is that it is cross-platform !
Unfortunately there is no coherence between the different drawing possibilities, B4A, B4i, BaJ, XUI and BitmapCreator.
You can see it HERE.
 
Upvote 0

PJLPJLPJL

Member
Licensed User
I have done something similar by drawing a text arrow (FontAwesome has several) where the drag ends - the font includes arrows pointing in the four ordinal directions but they can also be rotated if desired.
 
Upvote 0
Top