Faster touch events

Mansie

Member
Licensed User
Longtime User
I'm very much a newbie.... so I hope my question isn't too simple!

I want to write a simple drawing program - allowing the user to sketch freehand lines with their finger against a plain background, but when moving my finger over the screen in quickly in sweeping movements, I'm getting a series of straight lines rather than a nice smooth curve

I'm thinking that this is likely due to the touch events not happening quickly enough. I've seen commercial drawing programs for children which are fast and fluid - so I'm supposing its possible using b4a. Can I speed up the touch event frequency? Or is there a preferred method for faster line drawing?

Thanks in advance...

Mansie
 

Mansie

Member
Licensed User
Longtime User
Re Reflection libraries

Hi Erel

I used the reflection diary as you suggested - I modified a code example I found which included touch events but found that only an edittext box would give me the continual output of x and y coordinates I need to draw lines. Other objects (I tried replacing the edittext1 with a panel) only gave me the coordinates on touch down not on touch move.

I got it working by cheating... putting a large edittext box in front of a panel with a canvas, disabling the input keyboard, and using the edittext box touch events to give me my coordinates for drawing on the canvas. But I'm sure this is a pretty inefficient way of doing what I'm trying to do - it doesn't seem much if any quicker than the conventional way of doing it. And I have no idea why I need the second edittext box to make it work!

Apologies in advance for my convoluted methology here... but how do make this more efficient. In particular - how can I get this to work without using textedit boxes?

Here is the code for my simple screen drawing program using reflections

B4X:
'Activity module
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.
   
   Dim fromx,fromy,tox,toy As Int
   
End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   
   Dim Obj1 As Reflector
   Dim Obj2 As Reflector
   
   
   Dim EditText1 As EditText
   Dim EditText2 As EditText
   Dim Panel1 As Panel
    Dim picCanvas As Canvas

   Dim Button2 As Button


End Sub

Sub Activity_Create(FirstTime As Boolean)
   
   Dim DestRect As Rect
   Activity.LoadLayout("Layout")
   
   EditText1.Top=Panel1.Top
   EditText1.Left=Panel1.Left
   EditText1.Width=Panel1.Width
   EditText1.Height=Panel1.Height
   
   
   fromx=0
   fromy=0
   tox=0
   toy=0
   
   picCanvas.Initialize(Panel1)
   DestRect.Initialize(0dip, 0dip, 100%x, 100%y)
   picCanvas.DrawRect(DestRect, Colors.Transparent, True, 1dip)
   Panel1.Invalidate
   

   ' sets events on EditText1 using the common View events in the Reflection library
   Obj1.Target = EditText1 

   EditText1.Tag = "EditText1"
   

   Obj1.SetOnTouchListener("OnTouch")
   Obj1.SetOnFocusListener("OnFocus")
   Obj1.SetOnKeyListener("OnKey")   
   
   ' sets the same events on EditText2 using the generalised Proxy in the Reflection library
   Obj1.Target = EditText2 
   EditText2.Tag = "EditText2"
   
   ' specify the interfaces whose events we want to catch
   Dim ifaces(3) As String
   ifaces(0) = "android.view.View$OnTouchListener"
   ifaces(1) = "android.view.View$OnFocusChangeListener"
   ifaces(2) = "android.view.View$OnKeyListener"
   
   ' get the proxy that implements those interface and the Sub to be called
   Obj2.Target = Obj2.GetProxy(ifaces, "OnProxy")
   
   Dim args(1) As Object
   ' the proxy instance is the argument for all the "setOnXxxxListener" calls
   args(0) = Obj2.Target
   
   Dim types(1) As String
   ' set the interface type of the argument to the appropriate one for each call
   types(0) = "android.view.View$OnTouchListener"
   Obj1.RunMethod4("setOnTouchListener", args, types)
   types(0) = "android.view.View$OnFocusChangeListener"
   Obj1.RunMethod4("setOnFocusChangeListener", args, types)
   types(0) = "android.view.View$OnKeyListener"
   'I've had to comment this line out as there seems to be a bug in Android
   'that causes an Exception when closing the app when a Proxy is used on this EditText interface
   'Obj1.RunMethod4("setOnKeyListener", args, types)
   
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub OnProxy(method As String, args() As Object) As Object
   Dim action, ix, iy As Int
   Dim viewtag, msg As String
   Obj1.Target = args(0) ' args(0) is the view
   viewtag = Obj1.RunMethod("getTag")
   If method = "onTouch" Then
      Obj1.Target = args(1) ' args(1) is a Motion Event   
     ' ACTION_DOWN = 0; ACTION_UP = 1; ACTION_MOVE = 2;   
      action = Obj1.RunMethod("getAction")
      ix = Obj1.RunMethod("getX")
      iy = Obj1.RunMethod("getY")
   End If   

   Return False
End Sub

Sub OnTouch(viewtag As Object, action As Int, X As Float, Y As Float, motionevent As Object) As Boolean
   Dim ix, iy As Int
   ix = X
   iy = Y
  ' ACTION_DOWN = 0; ACTION_UP = 1; ACTION_MOVE = 2;   
  '   EditText3.Text = "x=" & ix & " " & "y=" & iy
  '=============================Line drawing here ===============================
   If action=0 Then 
   'puts a circle if the user just touches the screen briefly
    fromx=X
    fromy=Y
    picCanvas.DrawCircle(fromx, fromy, 10dip, Colors.Red, True, 1dip)
     Panel1.Invalidate   
   End If   
  
     If action=1 Then 
   'puts final circle as the user lifts their finger off the screen
    tox=X
    toy=Y
    picCanvas.DrawCircle(tox, toy, 10dip, Colors.Red, True, 1dip)
     Panel1.Invalidate   
   End If  
  
   If action=2 Then 
    'Filled circles stop jaggies between lines
    tox=X
    toy=Y
    picCanvas.DrawCircle(fromx, fromy, 10dip, Colors.Red, True, 1dip)
    picCanvas.DrawLine(fromx,fromy,tox,toy,Colors.Red,20dip)
    picCanvas.DrawCircle(tox, toy, 10dip, Colors.Red, True, 1dip)
    fromx=tox
    fromy=toy
    
     Panel1.Invalidate   
   End If 
    
   ' ToastMessageShow(viewtag & " Touch : " & action & " " & ix & " " & iy, False)
   Return False
End Sub

Sub OnKey(viewtag As Object, keycode As Int, keyevent As Object) As Boolean
   Return False
End Sub


Sub OnFocus(viewtag As Object, focus As Boolean)
'   ToastMessageShow(viewtag & " Focus : " & focus, False)
End Sub

Sub Button2_Click
   
  'Clears the drawing
   
            Dim DestRect As Rect
            DestRect.Initialize(0dip, 0dip, 100%x, 100%y)            
            picCanvas.DrawRect(DestRect, Colors.Transparent, True, 1dip)
            Panel1.Invalidate
   
End Sub


Many thanks

Mansie
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
Here you are:
B4X:
Sub Process_Globals
    Dim fromx,fromy,tox,toy As Int
End Sub

Sub Globals
    Dim Obj1 As Reflector
    
    Dim Panel1 As Panel
    Dim picCanvas As Canvas
    Dim DestRect As Rect
End Sub

Sub Activity_Create(FirstTime As Boolean)
    
'    Activity.LoadLayout("Layout")
    
    Panel1.Initialize("Panel1")
    Activity.AddView(Panel1, 0, 0, 100%x, 100%y)
        
    picCanvas.Initialize(Panel1)
    DestRect.Initialize(0dip, 0dip, 100%x, 100%y)
    picCanvas.DrawRect(DestRect, Colors.Transparent, True, 1dip)
    Panel1.Invalidate

    ' sets the Touch event for Panel1 using the Reflection library
    Obj1.Target = Panel1 
    Obj1.SetOnTouchListener("Panel1_OnTouch")
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub Panel1_OnTouch(viewtag As Object, action As Int, X As Float, Y As Float, motionevent As Object) As Boolean
  Select action
    Case Activity.ACTION_DOWN
    'puts a circle if the user just touches the screen briefly
     fromx = X
     fromy = Y
     picCanvas.DrawCircle(fromx, fromy, 10dip, Colors.Red, True, 1dip)
    Case Activity.ACTION_UP
        'puts final circle as the user lifts their finger off the screen
        tox = X
        toy = Y
        picCanvas.DrawCircle(tox, toy, 10dip, Colors.Red, True, 1dip)
    Case Activity.ACTION_MOVE
        'Filled circles stop jaggies between lines
        tox = X
        toy = Y
        picCanvas.DrawCircle(fromx, fromy, 10dip, Colors.Red, True, 1dip)
        picCanvas.DrawLine(fromx,fromy,tox,toy,Colors.Red,20dip)
        picCanvas.DrawCircle(tox, toy, 10dip, Colors.Red, True, 1dip)
        fromx = tox
        fromy = toy
    End Select 
    Panel1.Invalidate    
    Return True
End Sub
There is no problem to add a Touch event for a Panel !
This one replaces the event from B4A.

It would have been easier if you had also posted your project as a zip file.

Best regards.
 

Attachments

  • Draw.zip
    6.1 KB · Views: 635
Upvote 0

Mansie

Member
Licensed User
Longtime User
Re Faster touch events

Thanks for this Klaus (and Erel) - this is so much simpler and faster than my efforts.

Next time I have a question I'll certainly include a .zip project file (I'm a newbie just cutting my teeth, this is my first post and and I'm still learning all this stuff!).

Best wishes

Mansie
 
Upvote 0

cmartins

Member
Licensed User
Longtime User
Transparency

Hi everybody


I just downloaded the project and was fine until I change to do some transparency.

In my project I need paint with finger like a text marker.

I saw in others projects a delay before paint something like:

https://play.google.com/store/apps/details?id=com.ape.webapp.fingerpaint&feature=search_result

Im using a onTouch event.

I attached 2 pictures using 77 of transparency and in another one using 255.

are there other way to paint a line with transparency without jaggies?


regards
 

Attachments

  • FingerDraw.zip
    12.4 KB · Views: 468
  • Screenshot_2013-03-11-14-30-08.jpg
    Screenshot_2013-03-11-14-30-08.jpg
    57.7 KB · Views: 525
  • Screenshot_2013-03-11-14-31-26.png
    Screenshot_2013-03-11-14-31-26.png
    76.1 KB · Views: 505
Last edited:
Upvote 0

sorex

Expert
Licensed User
Longtime User
no, because if you overlay fragments of 20% tranparent colors you'll get 40% or higher values of that color.

the only thing that can be done as workaround is drawing at 100% opaque and then display it as 20% transparent.
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
good question :)

when drawing only in 1 color it's not really difficult to do.

the misery will start when you will draw a color on top of another one then you have to choose if you will go for an overlapping color or a blending one.

blending can occur when using e.g. color C0FF0000 for red instead of FFFF0000 but like I said it will also blend the same color like in your first screenshot happends.

the first value of the 24 bit value is the transparency (C0 is some, FF is no transparecy)

so I think you better take note of how the drawing should happen before wasting time on writing extra routines.
 
Upvote 0

Informatix

Expert
Licensed User
Longtime User
As I explained to you in my email, the applications that draw nice lines below your finger redraw the lines with an algorithm, and use an antialiasing filter to blend the edges with the background, so they look less jaggy (and a bit blurry). That's why you can notice easily a delay between your finger move and the result. You can find all the methods you need in the ABExtDrawing library.
 
Upvote 0

padvou

Active Member
Licensed User
Longtime User
Here's a simple approach of the problem. The real thing is more complicated. It's just to give you an idea with a few bezier curves. I painted each drawn segment in a different color:
attachment.php


This example needs the ABExtDrawing library.
I've changed this:
B4X:
Panel1.Color=Colors.Transparent
    picCanvas.Initialize(Panel1)
    DestRect.Initialize(0dip, 0dip, 100%x, 100%y - 120dip)
    picCanvas.DrawRect(DestRect, Colors.Transparent, True, 1dip)
    'picCanvas.DrawBitmap(bm, Null, DestRect)
    Panel1.Invalidate
and this:
B4X:
Select True
    Case toggle2.Checked=False
            DrawBezierCurve(fromx, fromy, x1, y1, x2, y2, X, Y, Colors.yellow, 15dip)
    Case toggle2.Checked=True
    DrawBezierCurve(fromx, fromy, x1, y1, x2, y2, X, Y, Colors.Transparent, 15dip)
    End Select
However the second case does not function as an eraser.
 
Upvote 0

Informatix

Expert
Licensed User
Longtime User
I've changed this:
B4X:
Panel1.Color=Colors.Transparent
    picCanvas.Initialize(Panel1)
    DestRect.Initialize(0dip, 0dip, 100%x, 100%y - 120dip)
    picCanvas.DrawRect(DestRect, Colors.Transparent, True, 1dip)
    'picCanvas.DrawBitmap(bm, Null, DestRect)
    Panel1.Invalidate
and this:
B4X:
Select True
    Case toggle2.Checked=False
            DrawBezierCurve(fromx, fromy, x1, y1, x2, y2, X, Y, Colors.yellow, 15dip)
    Case toggle2.Checked=True
    DrawBezierCurve(fromx, fromy, x1, y1, x2, y2, X, Y, Colors.Transparent, 15dip)
    End Select
However the second case does not function as an eraser.
"Colors.Transparent" does not erase another color, it just draws with a transparent color. Erasing needs a special mode (PorterDuff.Mode.CLEAR) that you can enable with the JavaObject lib or with the ABExtDrawing lib.
And note that this demo is not the better approach. A paint program will redraw your entire path with an algorithm (e.g. it will create a Catmull-Rom spline or it will use the Bezier algo on multiple segments or with big settings to transform any sharp angle into a smooth curve).
 
Upvote 0

padvou

Active Member
Licensed User
Longtime User
"Colors.Transparent" does not erase another color, it just draws with a transparent color. Erasing needs a special mode (PorterDuff.Mode.CLEAR) that you can enable with the JavaObject lib or with the ABExtDrawing lib.
And note that this demo is not the better approach. A paint program will redraw your entire path with an algorithm (e.g. it will create a Catmull-Rom spline or it will use the Bezier algo on multiple segments or with big settings to transform any sharp angle into a smooth curve).
Thank you for answering. Painting over with transparent painting, obviously does not do the trick here.. could you please demonstrate some code on how to erase using this example?
 
Upvote 0

Informatix

Expert
Licensed User
Longtime User
Thank you for answering. Painting over with transparent painting, obviously does not do the trick here.. could you please demonstrate some code on how to erase using this example?
Unfortunately, I don't have enough time to do so. What I did in My Playground for the painting activity is completely different and a bit complex (to erase, I paint with a mask made with the background image, which is not always an appropriate solution and needs a lot of memory).
 
Upvote 0

Linostar

Member
Licensed User
Longtime User
Thank you for answering. Painting over with transparent painting, obviously does not do the trick here.. could you please demonstrate some code on how to erase using this example?

Another solution is to work on the bitmap as pixel matrix. I didn't find a createBitmap function in B4A, so if it's not there, you can probably call it using a Reflector (I am not an expert with the Reflection library so I can't help you much there). Check the second answer in the following link for an example on how to use createBitmap to change pixel values and erase:
http://stackoverflow.com/questions/6432965/android-bitmap-mask-color-remove-color
 
Upvote 0
Top