B4A Library [Lib] Gesture Detector

This library adds the detection of standard gestures (press, single-tap, double-tap, long tap, drag, scroll, fling, pinch, rotation) to B4A. Instead of using the Touch events to figure out what the user really did, now you just set 15 different listeners with one line of code and you get the gestures as events with all the useful values (scrolling distance, fling velocity, pinch variation, rotation angle...).
With this library, you can also know easily the pressure or the size of a touch event.

It works with any view.

Note: you have to create an instance of GestureDetector (with Dim) for each view you want to bind to the detector with SetOnGestureListener.

v2.2:
- Freeware.

v2.3:
- I fixed an issue with the boolean value returned by the OnTouch event with some views (e.g. ListView or ScrollView);
- I added a new demo (GD_SwipeLV);
- The OnDown event is now raised before OnDoubleTap.

v2.4:
- I fixed an issue when a view is dragged after one of the pointer is up (delta values were computed according to the pointer 0, not to the remaining pointer).

Incompatible with Android versions < 2.
 

Attachments

  • GestureDetector v2.4.zip
    35.3 KB · Views: 3,029
  • Java source - GestureDetector.zip
    5.5 KB · Views: 1,163
Last edited:

synapse

Member
Licensed User
Longtime User
re: gesture_onfling. Can anyone advise please how to blur the appearance of an object after flinging? Whatever I do the view pauses and is then translated instantly to the new final position; without showing its track. Many thanks....
 
Last edited:

EnriqueGonzalez

Well-Known Member
Licensed User
Longtime User
have you tried animation plus? with translate, if you know the starting cordinates and the last ones, it even has some effects for accelerate and desaccelarete.
 

EnriqueGonzalez

Well-Known Member
Licensed User
Longtime User
Will try. Many thanks indeed! Gesture Detector can't do it?

oh yeah sure, both! gesturedetector and animation plus something like this: (but well done)

B4X:
Sub Process_Globals

End Sub

Sub Globals
Dim gd As GestureDetector
Dim aniplus As AnimationPlus
Dim b As Button

End Sub

Sub Activity_Create(FirstTime As Boolean)

b.initialize("")
Activity.AddView(b,45%x,45%y,10%x,10%x)
gd.SetOnGestureListener(b,"gd")
gd.EnableLongPress(False)



End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub gd_onFling(velocityX As Float, velocityY As Float, MotionEvent1 As Object, MotionEvent2 As Object)
aniplus.InitializeTranslate("move",gd.getX(MotionEvent1,0),gd.getY(MotionEvent1,0),gd.getX(MotionEvent2,0),gd.gety(MotionEvent2,0))
aniplus.PersistAfter = True
aniplus.Duration = 5000
aniplus.Start(b)


End Sub
 

James Chamblin

Active Member
Licensed User
Longtime User
I seem to be having problems getting PinchOpen and PinchClose to work. When you first start pinching, everything works as expected as long as you keep your fingers on the screen. When you lift your fingers, then replace them in a new position, the event acts as though you had simply moved your fingers there on the screen instead of lifting them. It seems that some flag is not being reset when the pinch/zoom ends.
B4X:
#Region  Project Attributes 
    #ApplicationLabel: Test Pinch-Zoom
    #VersionCode: 1
    #VersionName: 
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes 
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Dim zoom As Float = 1.0
    Private bmp As Bitmap

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.
    Private img As ImageView
    Private gd As GestureDetector

End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Lets draw a nice little smiley face for our sample
    Dim cnv As Canvas
    bmp.InitializeMutable(32dip,32dip)
    cnv.Initialize2(bmp)
    cnv.DrawColor(Colors.Red)
    cnv.DrawCircle(16dip,16dip,16dip,Colors.Yellow,True,1dip)
    cnv.DrawCircle(8dip,8dip,2dip,Colors.Black,True,1dip)
    cnv.DrawCircle(24dip,8dip,2dip,Colors.Black,True,1dip)
    Dim path1 As Path
    path1.Initialize(0,16dip)
    path1.LineTo(0,32dip)
    path1.LineTo(32dip,32dip)
    path1.LineTo(32dip,16dip)
    path1.LineTo(0,16dip)
    cnv.ClipPath(path1)
    cnv.DrawCircle(16dip,16dip,12dip,Colors.Black,True,1)
   
    'now we will create an image view and place it on the activity centered
    img.Initialize("")
    img.Gravity = Gravity.FILL
    img.SetBackgroundImage(bmp)
    Activity.AddView(img,(Activity.Width-bmp.Width)/2,(Activity.Height-bmp.Height)/2,bmp.Width,bmp.Height)
   
    'now add the GestureDetector
    gd.SetOnGestureListener(Activity,"gd")
    gd.EnableLongPress(False)

End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

'Here we recieve Pinchopen events.  Try opening a little bit, then lift your fingers and replace on the screen a bit
' further apart.  The image jumps as though you moved your fingers on the screen instead of just lifting them
Sub gd_onPinchOpen(NewDistance As Float, PreviousDistance As Float, MotionEvent As Object)
    'We will find the ratio between the NewDistance and Previous Distance and resize the image view accordingly
    zoom = zoom * (NewDistance/PreviousDistance)
    img.Width = bmp.Width*zoom
    img.Height = bmp.Height*zoom
    img.Top = (Activity.Height-img.Height)/2
    img.left = (Activity.Width - img.Width)/2
End Sub

'here we recieve PinchClose events. Same problem as above.  any repositioning of the fingers are read as movement across
' the screen.  PinchOpen/PinchClose should be reset when fingers are lifted from the screen.
Sub gd_onPinchClose(NewDistance As Float, PreviousDistance As Float, MotionEvent As Object)
    zoom = zoom *(NewDistance/PreviousDistance)
    img.Width = bmp.Width*zoom
    img.Height = bmp.Height*zoom
    img.Top = (Activity.Height-img.Height)/2
    img.left = (Activity.Width - img.Width)/2
End Sub
 

Informatix

Expert
Licensed User
Longtime User
I seem to be having problems getting PinchOpen and PinchClose to work. When you first start pinching, everything works as expected as long as you keep your fingers on the screen. When you lift your fingers, then replace them in a new position, the event acts as though you had simply moved your fingers there on the screen instead of lifting them. It seems that some flag is not being reset when the pinch/zoom ends.
B4X:
#Region  Project Attributes
    #ApplicationLabel: Test Pinch-Zoom
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Dim zoom As Float = 1.0
    Private bmp As Bitmap

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.
    Private img As ImageView
    Private gd As GestureDetector

End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Lets draw a nice little smiley face for our sample
    Dim cnv As Canvas
    bmp.InitializeMutable(32dip,32dip)
    cnv.Initialize2(bmp)
    cnv.DrawColor(Colors.Red)
    cnv.DrawCircle(16dip,16dip,16dip,Colors.Yellow,True,1dip)
    cnv.DrawCircle(8dip,8dip,2dip,Colors.Black,True,1dip)
    cnv.DrawCircle(24dip,8dip,2dip,Colors.Black,True,1dip)
    Dim path1 As Path
    path1.Initialize(0,16dip)
    path1.LineTo(0,32dip)
    path1.LineTo(32dip,32dip)
    path1.LineTo(32dip,16dip)
    path1.LineTo(0,16dip)
    cnv.ClipPath(path1)
    cnv.DrawCircle(16dip,16dip,12dip,Colors.Black,True,1)

    'now we will create an image view and place it on the activity centered
    img.Initialize("")
    img.Gravity = Gravity.FILL
    img.SetBackgroundImage(bmp)
    Activity.AddView(img,(Activity.Width-bmp.Width)/2,(Activity.Height-bmp.Height)/2,bmp.Width,bmp.Height)

    'now add the GestureDetector
    gd.SetOnGestureListener(Activity,"gd")
    gd.EnableLongPress(False)

End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

'Here we recieve Pinchopen events.  Try opening a little bit, then lift your fingers and replace on the screen a bit
' further apart.  The image jumps as though you moved your fingers on the screen instead of just lifting them
Sub gd_onPinchOpen(NewDistance As Float, PreviousDistance As Float, MotionEvent As Object)
    'We will find the ratio between the NewDistance and Previous Distance and resize the image view accordingly
    zoom = zoom * (NewDistance/PreviousDistance)
    img.Width = bmp.Width*zoom
    img.Height = bmp.Height*zoom
    img.Top = (Activity.Height-img.Height)/2
    img.left = (Activity.Width - img.Width)/2
End Sub

'here we recieve PinchClose events. Same problem as above.  any repositioning of the fingers are read as movement across
' the screen.  PinchOpen/PinchClose should be reset when fingers are lifted from the screen.
Sub gd_onPinchClose(NewDistance As Float, PreviousDistance As Float, MotionEvent As Object)
    zoom = zoom *(NewDistance/PreviousDistance)
    img.Width = bmp.Width*zoom
    img.Height = bmp.Height*zoom
    img.Top = (Activity.Height-img.Height)/2
    img.left = (Activity.Width - img.Width)/2
End Sub
No reset is supposed to be done because it's up to you to decide whether you want this behavior or not. If you want to ignore the previous distance, then set a flag when you receive onPointerDown. Note that I do not use such a flag in the demo and it works fine.
 

James Chamblin

Active Member
Licensed User
Longtime User
No reset is supposed to be done because it's up to you to decide whether you want this behavior or not. If you want to ignore the previous distance, then set a flag when you receive onPointerDown. Note that I do not use such a flag in the demo and it works fine.
Thanks, setting my own flag worked. This behavior does seem odd to me though. When I think of pinch, I think of two fingers moving closer while on the screen, not two fingers simply touching one position, then touching again a little closer.
 

Cableguy

Expert
Licensed User
Longtime User
Hi @Informatix, I'm having trouble setting your lib for a simple Slide event.

I have used as suggested the onScroll event, but It is being fired multiple times an a single slide(It is detecting several small movements on the big slide movement)
I see the Down and Move events inside the onScroll, but no Up event.
What would be the simplest way to get a 1 to 1 Slide event?
Touch down - Move - Touch Up = 1 onScroll event
 

Informatix

Expert
Licensed User
Longtime User
Hi @Informatix, I'm having trouble setting your lib for a simple Slide event.

I have used as suggested the onScroll event, but It is being fired multiple times an a single slide(It is detecting several small movements on the big slide movement)
I see the Down and Move events inside the onScroll, but no Up event.
What would be the simplest way to get a 1 to 1 Slide event?
Touch down - Move - Touch Up = 1 onScroll event
Then check the OnDrag event instead.
 

rleiman

Well-Known Member
Licensed User
Longtime User
Hi,

I tried to place the call to the Gesture Detector in this code but it stopped the rows from being tapped on even though the sub routine for double tapping executes. The commented out line is the coding I used.

B4X:
Sub Item_RowLayoutCreator(LayoutName As String, CellPanel As Panel, CellIndex As Byte)
    'The layout of the data cells is very simple: just a label
    Dim lblText As Label
    lblText.Initialize("")
    lblText.Color = Colors.Transparent
    lblText.TextColor = Colors.Black
    lblText.TextSize = 16
    lblText.Typeface = Typeface.DEFAULT
    lblText.Gravity = Gravity.CENTER_VERTICAL
    CellPanel.AddView(lblText, 10dip, 5dip, CellPanel.Width - 20dip, CellPanel.Height - 10dip)

'    GD.SetOnGestureListener(lblText, "LabelText")
End Sub

Sub Item_RowContentFiller(RowID As Long, LayoutName As String, CellPanel As Panel, CellIndex As Byte, Position As Int)
    If ULV.IsSelected(Position) Then
        'The selected rows are green
        CellPanel.Color = Colors.Green
    Else
        'Alternatively, the rows are coloured in white or in light gray
        If Position Mod 2 = 0 Then
            CellPanel.Color = Colors.White
        Else
            CellPanel.Color = Colors.LightGray
        End If
    End If

    'Reads the data and sets the label text
    Dim lbl As Label = CellPanel.GetView(0) 'The first (and only) view of the cell is a label

    Select CellIndex
        Case 0: dbCursor.Position = Position
            lbl.Text = dbCursor.GetString("StudentSurname")
        Case 1:
            lbl.Text = dbCursor.GetString("StudentForename")
    End Select
End Sub

B4X:
Sub LabelText_onDoubleTap(X As Float, Y As Float, MotionEvent As Object)
    ToastMessageShow("at label text",False)
End Sub

I'm hoping to see if I can detect a double tap when the user does it in the ULV rows.
 

Informatix

Expert
Licensed User
Longtime User
Hi,

I tried to place the call to the Gesture Detector in this code but it stopped the rows from being tapped on even though the sub routine for double tapping executes. The commented out line is the coding I used.

B4X:
Sub Item_RowLayoutCreator(LayoutName As String, CellPanel As Panel, CellIndex As Byte)
    'The layout of the data cells is very simple: just a label
    Dim lblText As Label
    lblText.Initialize("")
    lblText.Color = Colors.Transparent
    lblText.TextColor = Colors.Black
    lblText.TextSize = 16
    lblText.Typeface = Typeface.DEFAULT
    lblText.Gravity = Gravity.CENTER_VERTICAL
    CellPanel.AddView(lblText, 10dip, 5dip, CellPanel.Width - 20dip, CellPanel.Height - 10dip)

'    GD.SetOnGestureListener(lblText, "LabelText")
End Sub

Sub Item_RowContentFiller(RowID As Long, LayoutName As String, CellPanel As Panel, CellIndex As Byte, Position As Int)
    If ULV.IsSelected(Position) Then
        'The selected rows are green
        CellPanel.Color = Colors.Green
    Else
        'Alternatively, the rows are coloured in white or in light gray
        If Position Mod 2 = 0 Then
            CellPanel.Color = Colors.White
        Else
            CellPanel.Color = Colors.LightGray
        End If
    End If

    'Reads the data and sets the label text
    Dim lbl As Label = CellPanel.GetView(0) 'The first (and only) view of the cell is a label

    Select CellIndex
        Case 0: dbCursor.Position = Position
            lbl.Text = dbCursor.GetString("StudentSurname")
        Case 1:
            lbl.Text = dbCursor.GetString("StudentForename")
    End Select
End Sub

B4X:
Sub LabelText_onDoubleTap(X As Float, Y As Float, MotionEvent As Object)
    ToastMessageShow("at label text",False)
End Sub
If you mean "CellClick does not work any longer", then it's right for the label (GD is the main listener, so any click on the label will be catched by GD and the CellClick event won't be raised), but not for the panel holding the label.
For a given view, you can use either GD to catch the touch events, or the ULV embedded listeners. Not both. If you want to use GD to detect double taps on your label, that does not change anything for the other views (e.g. the panel holding the label). For them, touch events are still detected by ULV.
To detect clicks (onSingleTapConfirmed) and double taps on the label, you must use GD and bind a different GD for every label (don't forget the Dim before the SetonGestureListener).
 

rleiman

Well-Known Member
Licensed User
Longtime User
Hi,

Can you show the needed code to allow me to double tap on any cell of the ULV row and catch that in a sub procedure?

Thanks.
 

leongcc

Member
Licensed User
Longtime User
Great work Informatix, Thanks.

I would like to ask if anyone has any suggestion on how a user's gestures are captured independently and in parallel regardless what are in the views. I would normally overlay with a transparent panel and use Gesture or GestureDetector to 'tap' the fingers' action.

A difficult situation is when a webview is on focus and the various listeners and javascripts are in operation, they interfere with Gesture and GestureDetector. When Gesture or GestureDetector is listening, user cannot drag/pan/zoom GoogleMap.
 

JackKirk

Well-Known Member
Licensed User
Longtime User
USING GESTURE DETECTOR AS A GESTURE FILTER

Hi, I'm experimenting with using the Gesture Detector library as a means of filtering gestures to Google Maps v2.

Basically all I'm doing is passing each gesture that I want to give to Google Maps via GD.PassTouchEventTo:

B4X:
Sub Gesture_onDown(X As Float, Y As Float, MotionEvent As Object)
    GD.PassTouchEventTo(MotionEvent,Map_Panel)
End Sub

Sub Gesture_onPointerDown(ptrIndex As Int, PID As Int, MotionEvent As Object)
    GD.PassTouchEventTo(MotionEvent,Map_Panel)
End Sub

Sub Gesture_onPointerUp(ptrIndex As Int, PID As Int, MotionEvent As Object)
    GD.PassTouchEventTo(MotionEvent,Map_Panel)
End Sub

'etc...
I'm surprising myself with how easy this seems to work.

Except with the xxx_onScroll and xxx_onFling gestures which both have 2 MotionEvents:
B4X:
Sub Gesture_onScroll(distanceX As Float, distanceY As Float, MotionEvent1 As Object, MotionEvent2 As Object)
    GD.PassTouchEventTo(MotionEvent1,Map_Panel)
    GD.PassTouchEventTo(MotionEvent2,Map_Panel)
End Sub

Sub Gesture_onFling(velocityX As Float, velocityY As Float, MotionEvent1 As Object, MotionEvent2 As Object)
    GD.PassTouchEventTo(MotionEvent1,Map_Panel)
    GD.PassTouchEventTo(MotionEvent2,Map_Panel)
End Sub
My juvenile attempts at passing the MotionEvents sequentially (as indicated above) doesn't cause anything to bomb but does produce some rather bizarre behaviour.

Is there any way to pass paired MotionEvents properly (maybe some exotic Java?).

Thanks in advance...

BTW - Informatix (I presume you will read this) - this library seems to be one of the more professional 3rd party efforts I've seen so far - very impressive.
 
Last edited:
Top