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,039
  • Java source - GestureDetector.zip
    5.5 KB · Views: 1,170
Last edited:

CapReed

Member
Licensed User
Longtime User
In the first place thank you for your tremendous ability to create this library and other codes. They are extremely useful.

Fatal signal 11 (SIGSEGV) at 0x00780065 (code=1), thread 25080 (b4a.gesturedemo)

I get this error when using your example with B4A 2.50. to make a fling . I also tested with an application I'm developing and I get the same error intermittently when I make a fling.

You know what's the problem?

To finish, how I can find out if the user has left your finger down on the screen? I want to change a number when the user clicks on an image, but do not need the user to press several times, just to keep your finger on the image.

Many Thanks
 

Informatix

Expert
Licensed User
Longtime User
New version

New version 2.03 (donationware):
- I added many functions:
getHistorySize, getHistoricalX, getHistoricalY, getHistoricalPressure, getHistoricalSize, getTouchMajor, getTouchMinor, getXPrecision, getYPrecision

- I added the Action constants:
ACTION_DOWN, ACTION_UP, ACTION_MOVE, ACTION_CANCEL

- I added a new example:
GestureDetector_Paint
 
Last edited:

Inman

Well-Known Member
Licensed User
Longtime User
Great library. The demo works really well. But I am trying to do something different, which I am not sure is possible.

I want to set the Gesture Detector on a vertical ScrollView. Since the ScrollView doesn't have horizontal scrolling, I want to detect the gestures that are closer to horizontal movement (i.e. x2-x1 > y2-y1), while passing the vertical scrolling gestures straight to the ScrollView to perform its natural scrolling action.

Is this possible?
 

Informatix

Expert
Licensed User
Longtime User
Great library. The demo works really well. But I am trying to do something different, which I am not sure is possible.

I want to set the Gesture Detector on a vertical ScrollView. Since the ScrollView doesn't have horizontal scrolling, I want to detect the gestures that are closer to horizontal movement (i.e. x2-x1 > y2-y1), while passing the vertical scrolling gestures straight to the ScrollView to perform its natural scrolling action.

Is this possible?

You should look at the Bookmark example provided with UltimateListView. It shows how to detect a horizontal swipe and ignore (or block) vertical moves. This example doesn't use Gesture Detector, but you should better use it. With it, it's a lot easier to do because you don't have to bother with a timer to distinguish a fast move from a slow one (with GD, a slow gesture is a "scroll", a fast gesture is a "fling").
Now the big question is : why do you need to detect a horizontal gesture?
 

Inman

Well-Known Member
Licensed User
Longtime User
You should look at the Bookmark example provided with UltimateListView. It shows how to detect a horizontal swipe and ignore (or block) vertical moves. This example doesn't use Gesture Detector, but you should better use it. With it, it's a lot easier to do because you don't have to bother with a timer to distinguish a fast move from a slow one (with GD, a slow gesture is a "scroll", a fast gesture is a "fling").
Now the big question is : why do you need to detect a horizontal gesture?

I will explain. Actually in the last 2 hours time I switched from ScrollView to WebView and here is why.

In my app the content that needed to be displayed was originally plain text. But for the next update I want to display bold, italics, hyperlinks, images etc...So instead of parsing manually, it will be easier to display the whole thing on a webview.

Now as for why I need just the horizontal gesture, this webview will only scroll in vertical direction. It is opened by clicking an item from the listview in the previous activity. So once the user finishes reading this webview, I wanted to return to the previous activity by using a horizontal flinging gesture, instead of reaching for the back button, which might be hard to do if the user is reading it onehandedly.
 

Informatix

Expert
Licensed User
Longtime User
Now as for why I need just the horizontal gesture, this webview will only scroll in vertical direction. It is opened by clicking an item from the listview in the previous activity. So once the user finishes reading this webview, I wanted to return to the previous activity by using a horizontal flinging gesture, instead of reaching for the back button, which might be hard to do if the user is reading it onehandedly.

Method 1: AHViewPager. On one page, you have the listview, on the other page, you have the webview (the page is created when you click on an item, and deleted when you come back to the listview). With a horizontal swipe, you can switch between them.

Method 2: You handle yourself the swipe gesture. Like I did in the bookmark example. You remove the timer stuff because you only need to detect the gesture.
 

Inman

Well-Known Member
Licensed User
Longtime User
Method 1 won't work for me because I already have an AHViewPager in the project with multiple tabs in the correct positions and therefore I can't introduce a new tab.

I would love to implement method 2. I checked your bookmarks example. One thing I don't understand is the ULV_ItemTouch sub. You check if it is a horizontal fling and if it is, you create the bookmark. Understood. But in the Else part you just call DisableVerticalScroll(False). How does this result in ULV receiving the touch event and scrolling vertically accordingly?

And will it be possible to replicate this on a webview which doesn't have a touch event?
 

Informatix

Expert
Licensed User
Longtime User
Method 1 won't work for me because I already have an AHViewPager in the project with multiple tabs in the correct positions and therefore I can't introduce a new tab.

I would love to implement method 2. I checked your bookmarks example. One thing I don't understand is the ULV_ItemTouch sub. You check if it is a horizontal fling and if it is, you create the bookmark. Understood. But in the Else part you just call DisableVerticalScroll(False). How does this result in ULV receiving the touch event and scrolling vertically accordingly?

And will it be possible to replicate this on a webview which doesn't have a touch event?

I never used a WebView so I'm like a blind man trying to guide you. If this view has no touch event, it's probably impossible to detect a gesture, and after looking at the description of this view, it seems it can scroll in both directions so that makes things even harder.
If the HTML contents that you want to display contains only basic tags, maybe you can use the LabelExtras library instead of the WebView.

One thing I don't understand is the ULV_ItemTouch sub. You check if it is a horizontal fling and if it is, you create the bookmark. Understood. But in the Else part you just call DisableVerticalScroll(False). How does this result in ULV receiving the touch event and scrolling vertically accordingly?

If a horizontal swipe is detected, I block the UltimateListView from intercepting the current event. If no horizontal swipe is detected, I let the ULV intercept the event.
 
Last edited:

Inman

Well-Known Member
Licensed User
Longtime User
I did it :)

I had to use 3 libraries to achieve what I described - Gesture Detector, WebViewXtender and WebViewExtras. Here is the relevant code.

B4X:
Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   Dim wb As WebView, wx As WebViewXtender, wbx As WebViewExtras
    Dim GD As GestureDetector
End Sub

Sub Activity_Create(FirstTime As Boolean)
   GD.SetOnGestureListener(wb,"Gesture")
   GD.EnableLongPress(False)
End Sub

Sub Gesture_onScroll(distanceX As Float, distanceY As Float, MotionEvent1 As Object, MotionEvent2 As Object)

   If Abs(distanceY)>Abs(distanceX) Then
      Dim newpos, wlength As Int
      newpos=wx.getScrolly(wb)+distanceY
      
      If newpos<0 Then 
         newpos=0
      Else
         wlength=wbx.GetContentHeight(wb) * wbx.GetScale(wb)
         If newpos>wlength Then newpos=wlength
      End If
      
      wx.scrollTo(wb,0,newpos)
   End If

End Sub

Sub Gesture_onFling(velocityX As Float, velocityY As Float, MotionEvent1 As Object, MotionEvent2 As Object)
   
   Dim x1,y1,x2,y2 As Float
   Dim xd,yd As Double
   
   x1=GD.getX(MotionEvent1)
   y1=GD.getY(MotionEvent1)
   x2=GD.getX(MotionEvent2)
   y2=GD.getY(MotionEvent2)
   
   xd=Abs(x2-x1)
   yd=Abs(y2-y1)
   
   If xd>yd AND Abs(velocityX)>Abs(velocityY) Then
      
      Activity.Finish
   Else
      wbx.flingScroll(wb,-velocityX,-velocityY)
   End If
   
End Sub

Sub Gesture_onScroll(distanceX As Float, distanceY As Float, MotionEvent1 As Object, MotionEvent2 As Object)

   If Abs(distanceY)>Abs(distanceX) Then
      Dim newpos, wlength As Int
      newpos=wx.getScrolly(wb)+distanceY
      
      If newpos<0 Then 
         newpos=0
      Else
         wlength=wbx.GetContentHeight(wb) * wbx.GetScale(wb)
         If newpos>wlength Then newpos=wlength
      End If
      
      wx.scrollTo(wb,0,newpos)
   End If

End Sub

WebViewXtender.ScrollTo works really well. Fortunately it also had a flingscroll() event which took velocityX and velocityY of Gesture_onFling() as argument. And finally Martin updated his WebViewExtras library to include GetContentHeight and GetScale events, which when multiplied gives the total height of the webview content, beyond which a scroll should not happen.
 

francoisg

Active Member
Licensed User
Longtime User
Hi, I see you are using the following subs in your code - I can't seem to find any of them in the versions of the libraries that I have (WebViewExtras- v 1.35, WebViewXtender v 1.20) - Do I have outdated library versions or is there something else that I am missing?

WebViewExtras.GetContentHeight
WebViewExtras.GetScale

WebViewXtender.scrollTo

Greetings from sunny South Africa!
 

Inman

Well-Known Member
Licensed User
Longtime User
Yes, those versions are outdated. But not your mistake though.

In WebViewXtender thread, scroll to the bottom of the first page to get v1.50. In WebViewExtras thread, check the last page to get v1.36 alpha, which Martin made as a test version for me to try out, which is probably why he didn't update the first post.
 

drachmad

Member
Licensed User
Longtime User
Gesture_onTouch still active although the panel is not visible?

Thanks for a great Library! I use version 1.1.
From the log you can see that the Gesture_onTouch still active although the panel is not visible (even remove). That why I must put the "If holder.visible "
Sub Gesture_onTouch(...)
Log("onTouch action=" & Action ...)
' If holder.NumberOfViews > 0 Then
If holder.visible Then
WithInPoint(X,Y)
End If
End Sub

And after using several times (10-20 times), it freeze and sometimes give an error "Fatal signal 11 (SIGSEGV)". Maybe it is related to the drawing?
 

Attachments

  • UnlockPattern.zip
    7.1 KB · Views: 230

Informatix

Expert
Licensed User
Longtime User
Thanks for a great Library! I use version 1.1.
From the log you can see that the Gesture_onTouch still active although the panel is not visible (even remove). That why I must put the "If holder.visible "
Sub Gesture_onTouch(...)
Log("onTouch action=" & Action ...)
' If holder.NumberOfViews > 0 Then
If holder.visible Then
WithInPoint(X,Y)
End If
End Sub

And after using several times (10-20 times), it freeze and sometimes give an error "Fatal signal 11 (SIGSEGV)". Maybe it is related to the drawing?

Your AddView function initializes a panel declared in Globals; this panel should be initialized in the Initialize sub, as GD. That will prevent some troubles if you want to call AddView more than once. And if AddView is called only once, always after Initialize, then call it from the Initialize function.
Cvs shouldn't be public.
And you should replace CallSub3 by CallSubDelayed3 (line #130). It is not recommended to call MsgBox in a touch event with CallSub.
With all these changes, your class runs fine.

An advice to speed up things:
Instead of computing c = Sqrt(a * a + b * b) and comparing c repeatedly to Radius, do:
c = a * a + b * b
r = Radius * Radius
then compare c to r.
 

drachmad

Member
Licensed User
Longtime User
Thanks a lot Informatix,
For your prompt reply and suggestion.
I have change it, before I put it the the library to share I will test it more intensively.
 

hookshy

Well-Known Member
Licensed User
Longtime User
gestures member not recognised

In the example with asteroid game I found that the declaration:
B4X:
Private ges As Gestures
[\code]
is not recognised .
The library GestureDetector V1.10 is activated.
I can not test the asteroid example . 
Only found GestureDetector as member not Gestures. 
Can you tell why ??
Thank you .
 

hookshy

Well-Known Member
Licensed User
Longtime User
In the example with asteroid game I found that the declaration:
code
Private ges As Gestures
\code
is not recognised .
The library GestureDetector V1.10 is activated.
I can not test the asteroid example .
Only found GestureDetector as member not Gestures.
Can you tell why ??
Thank you .
 
Top