Android Question How to draw a line with a gradient or texture?

Dave O

Well-Known Member
Licensed User
Hi all,

I am currently using canvas.drawLine to draw lines with a flat color.

However, that's a bit boring. I'd like to be able to do effects like "glow/neon", like this:
http://stackoverflow.com/questions/5014828/android-neon-tubing

After some B4A searching and Android googling, I think this involves using either:
- a radial gradient (or perhaps a linear gradient rotated?), or
- a glow effect using a Paint object/texture thingie

Is there a way to do this with a normal Canvas, or should I be using a more advanced drawing library like AcceleratedSurface or ABExtDrawing?

Any code samples?

As always, grateful for any help.
 

EnriqueGonzalez

Well-Known Member
Licensed User
With the core Canvas should be sufficient (using ACSurface should be better), but instead of thinking in "Lines" think in paths!

Paths allow you to clip a drawable that means that you could use gradientdrawable or bitmapdrawable to fill the shape that path made.
 
Upvote 0

EnriqueGonzalez

Well-Known Member
Licensed User
This Code creates the attached image.

B4X:
Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    'Activity.LoadLayout("Layout1")

Dim cv As Canvas 
cv.Initialize(Activity)
Dim p As Path
p.Initialize(10%x,10%y)
p.LineTo(12%x,10%y)
p.LineTo(12%x,80%y)
p.LineTo(10%x,80%y)
p.LineTo(10%x,10%y)
cv.ClipPath(p)
Dim rect As Rect
rect.Initialize(0%x,0%y,100%x,100%y)
cv.DrawRect(rect,Colors.Transparent,True,0dip)
Dim f As GradientDrawable
Dim c(2) As Int
c(0) = Colors.Cyan
c(1) = Colors.Blue
f.Initialize("TL_BR",c)
cv.DrawDrawable(f,rect)
Activity.Invalidate
End Sub
 

Attachments

  • gradient.png
    gradient.png
    15.5 KB · Views: 915
Upvote 0

RandomCoder

Well-Known Member
Licensed User
If your not wanting lots of lines then you could simply use a narrow Panel with the GradientDrawable applied.
 
Upvote 0

Dave O

Well-Known Member
Licensed User
Actually, what I'm aiming for is shown in the link in the original post - a "neon" effect where the line has an outer blur. (I'm not looking for a linear gradient that spans the length of the line.)

That's why I'm thinking it's either a blur effect (using a Paint object, for example) or a radial gradient that's used as the texture for a line.

I'll experiment with some libraries, but I was hoping someone else had already covered this ground and had some code (or techniques) that I could adapt.

Thanks!
 
Upvote 0

RandomCoder

Well-Known Member
Licensed User
Not quite the same but I use the code shown below to give a nice shaded colour on an RGB slider bar control as pictured (this is a library that I've not got round to issuing yet). The middle of the caret changes colour to match the colour at the selected position on the bar. I'm sure you could create the effect that you want using a similar technique.
B4X:
Private Sub setCaretGradient(intColor As Int) As GradientDrawable
    ' Change RGB caret colour based on bar position
    Dim clrs(7) As Int
    #Region clrs(0)..(6)
    clrs(0) = Colors.DarkGray
    clrs(1) = Colors.LightGray
    clrs(2) = intColor
    clrs(3) = intColor
    clrs(4) = intColor
    clrs(5) = Colors.LightGray
    clrs(6) = Colors.DarkGray
    #End Region
    Dim gd As GradientDrawable
    gd.Initialize("LEFT_RIGHT", clrs)
    gd.CornerRadius = 3dip
    Return gd
End Sub

 
Upvote 0

stevel05

Expert
Licensed User
Last edited:
Upvote 0

Dave O

Well-Known Member
Licensed User
You should be able to add a BlurMaskFilter in a similar manner.

Bingo, that's what I needed. The BlurMaskFilter does the glow effect that I was looking for.

To get the neon effect, I draw the "glow" first, then draw the "tube" over top of it:

B4X:
Sub drawLineAsNeon(canvasArg As Canvas, x1 As Float, y1 As Float, x2 As Float, y2 As Float, tubeWidth As Float, glowWidth As Float, colorArg As Int)
   Dim canvasJO, paintJO, blurMaskFilterJO As JavaObject
   canvasJO = canvasArg
   canvasJO = canvasJO.GetField("canvas")

   paintJO.InitializeNewInstance("android.graphics.Paint", Null)
   paintJO.RunMethod("setStrokeCap", Array("ROUND"))
   paintJO.RunMethod("setColor", Array(colorArg))

   'draw the outer glow
   paintJO.RunMethod("setStrokeWidth", Array(glowWidth))
   glowWidth = Max(glowWidth, 1)       'BlurMaskFilter can't handle zero width
   blurMaskFilterJO.InitializeNewInstance("android.graphics.BlurMaskFilter", Array(glowWidth, "NORMAL"))
   paintJO.RunMethod("setMaskFilter", Array(blurMaskFilterJO))
   canvasJO.RunMethod("drawLine", Array As Object(x1, y1, x2, y2, paintJO))

   'draw the inner line
   paintJO.RunMethod("setStrokeWidth", Array(tubeWidth))
   tubeWidth = Max(tubeWidth, 1)       'BlurMaskFilter can't handle zero width
   blurMaskFilterJO.InitializeNewInstance("android.graphics.BlurMaskFilter", Array(tubeWidth, "SOLID"))
   paintJO.RunMethod("setMaskFilter", Array(blurMaskFilterJO))
   canvasJO.RunMethod("drawLine", Array As Object(x1, y1, x2, y2, paintJO))
End Sub

...then call it like this:
B4X:
drawLineAsNeon(someCanvas, 100, 100, 500, 900, 10dip, 25dip, Colors.green)
drawLineAsNeon(someCanvas, 500, 900, 700, 200, 10dip, 25dip, Colors.green)

and the result is the screenshot attached.

Thanks all!
 

Attachments

  • neon sample.png
    neon sample.png
    86.7 KB · Views: 1,011
Upvote 0

Dave O

Well-Known Member
Licensed User
Actually, it turns out that I don't need to draw the line twice (glow + inner). I can get the same result by just using the "SOLID" BlurMaskFilter, like this:

B4X:
Sub drawLineAsNeon(canvasArg AsCanvas, x1 As Float, y1 As Float, x2 As Float, y2 As Float, strokeWidth As Float, glowWidth As Float, colorArg As Int)Dim canvasJO, paintJO, blurMaskFilterJO AsJavaObject
 canvasJO = canvasArg
 canvasJO = canvasJO.GetField("canvas")

 paintJO.InitializeNewInstance("android.graphics.Paint", Null)
 paintJO.RunMethod("setStrokeCap", Array("ROUND"))
 paintJO.RunMethod("setColor", Array(colorArg))
 paintJO.RunMethod("setStrokeWidth", Array(tubeWidth))

 glowWidth = Max(glowWidth, 1) 'BlurMaskFilter can't handle zero width
 blurMaskFilterJO.InitializeNewInstance("android.graphics.BlurMaskFilter", Array(glowWidth, "SOLID"))
 paintJO.RunMethod("setMaskFilter", Array(blurMaskFilterJO))

 canvasJO.RunMethod("drawLine", ArrayAs Object(x1, y1, x2, y2, paintJO))
End Sub

This is enough to get the neon effect. However, adding a semi-transparent white inner line (with no blur filter) also looks cool. :)
 
Upvote 0
Top