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

JackKirk

Well-Known Member
Licensed User
Longtime User
Is there any way to pass paired MotionEvents properly (maybe some exotic Java?).
Worked it out for myself:
B4X:
Sub Gesture_onScroll(distanceX As Float, distanceY As Float, MotionEvent1 As Object, MotionEvent2 As Object)
    GD.PassTouchEventTo(MotionEvent2,Map_Panel)
End Sub

Sub Gesture_onFling(velocityX As Float, velocityY As Float, MotionEvent1 As Object, MotionEvent2 As Object)
    GD.PassTouchEventTo(MotionEvent2,Map_Panel)
End Sub
You just need to pass the second MotionEvent.
 

AndrewKing

Member
Licensed User
Longtime User
This could be a bug in GestureDetector 2.2

This is the sample code for testing:

B4X:
Sub Globals
   Dim L As ListView
   Dim GD As GestureDetector
End Sub

Sub Activity_Create(FirstTime As Boolean)
   L.Initialize("L")
   Activity.AddView(L,0,0,100%x,100%y)

   Dim i As Int
   For i =0 To 30
     L.AddSingleLine(i)
   Next

   GD.SetOnGestureListener(L, "Gesture")
End Sub

Sub Gesture_onTouch(Action As Int, X As Float, Y As Float, MotionEvent As Object) As Boolean
   Return True 'True = Handle this touch event
End Sub

Sub L_ItemClick (Position As Int, Value As Object)
   Msgbox(Position, "ItemClick")
End Sub


As you see, there is only one line "Return True" in sub Gesture_onTouch. Then the listview can not scroll with finger and can not be clicked. But it doens't work as it should be.

To test it, please run this sample code, put your finger on the listview and move your finger up slowly - slowly - slowly, release your finger, then you will see the bug: the listview scrolls.

It's not all, if you put your finger on the listview for about 3 seconds and then release the finger, you will see the msgbox from sub L_ItemClick. (actually you just need to quilckly and continuously click on the listview and one click will show the pop-up msgbox)

As I tested, This problem happens when GestureDetector work with Listview, ScrollView and ScrollView2D.

1.png
2.png
 
Last edited:

tpakis

Active Member
Licensed User
Longtime User
I found a similar problem, when Return False at onTouch, then no scroll fling pinch is detected
 

Informatix

Expert
Licensed User
Longtime User
This could be a bug in GestureDetector 2.2

This is the sample code for testing:

B4X:
Sub Globals
   Dim L As ListView
   Dim GD As GestureDetector
End Sub

Sub Activity_Create(FirstTime As Boolean)
   L.Initialize("L")
   Activity.AddView(L,0,0,100%x,100%y)

   Dim i As Int
   For i =0 To 30
     L.AddSingleLine(i)
   Next

   GD.SetOnGestureListener(L, "Gesture")
End Sub

Sub Gesture_onTouch(Action As Int, X As Float, Y As Float, MotionEvent As Object) As Boolean
   Return True 'True = Handle this touch event
End Sub

Sub L_ItemClick (Position As Int, Value As Object)
   Msgbox(Position, "ItemClick")
End Sub


As you see, there is only one line "Return True" in sub Gesture_onTouch. Then the listview can not scroll with finger and can not be clicked. But it doens't work as it should be.

To test it, please run this sample code, put your finger on the listview and move your finger up slowly - slowly - slowly, release your finger, then you will see the bug: the listview scrolls.

It's not all, if you put your finger on the listview for about 3 seconds and then release the finger, you will see the msgbox from sub L_ItemClick. (actually you just need to quilckly and continuously click on the listview and one click will show the pop-up msgbox)

As I tested, This problem happens when GestureDetector work with Listview, ScrollView and ScrollView2D.

View attachment 45938 View attachment 45939
Disable the long press with:
GD.EnableLongPress(False)
 

AndrewKing

Member
Licensed User
Longtime User
Disable the long press with:
GD.EnableLongPress(False)

There are 3 problems. "GD.EnableLongPress(False)" can only resolve problem No. 2.

Problem No.1 is very strange - if you move finger quickly, the listview doesn't scroll - yes it shouldn't scroll. But if you move finger slowly, the listview scrolls.

Problem No.3 is very clear, the code "return true" in Sub Gesture_onTouch doesn't block all clicks. Some clicks can pass through it.

The souce code for testing is updated:
B4X:
Sub Globals
   Dim L As ListView
   Dim GD As GestureDetector
End Sub

Sub Activity_Create(FirstTime As Boolean)
   L.Initialize("L")
   Activity.AddView(L,0,0,100%x,100%y)

   Dim i As Int
   For i =0 To 30
     L.AddSingleLine(i)
   Next
  
   GD.SetOnGestureListener(L, "Gesture")
   GD.EnableLongPress(False)
End Sub

Sub Gesture_onTouch(Action As Int, X As Float, Y As Float, MotionEvent As Object) As Boolean
   Return True 'True = Handle this touch event
End Sub

Sub L_ItemClick (Position As Int, Value As Object)
   Msgbox(Position, "ItemClick")
End Sub

1.jpg
2.jpg
3.jpg
 
Last edited:

Informatix

Expert
Licensed User
Longtime User
There are 3 problems. "GD.EnableLongPress(False)" can nonly resolve problem No. 2.

Problem No.1 is very strange - if you move finger quickly, the listview doesn't scroll.

View attachment 46147 View attachment 46148 View attachment 46149
I'm sorry but I don't understand. Could you explain what is problem #1, problem #2 and so on? When I return True in the Touch event and disable the long press, nothing happens whatever I do (slow move, quick move, long press, etc.).
 

AndrewKing

Member
Licensed User
Longtime User
I just tested the code on 2 phones (android 4.4 and 5.1) and found the problems on both.

The animated gif file demonstrates what the problems are. It is recorded on my PC.

a.gif
 
Last edited:

Informatix

Expert
Licensed User
Longtime User
I just tested the code on 2 phones (android 4.4 and 5.1) and found the problems on both.

The animated gif file demonstrates what the problems are. It is recorded on my PC.

View attachment 46152
I finally succeeded in reproducing the issue. It was not easy as I have no issue on my main device.
You have to know that all events are generated by the OS, not by my lib. My lib listens to them then passes them to your B4A app so they can be handled, and return the boolean value returned by your OnTouch code. An OS or a ListView bug is not under my control, and I have no clear evidence that my lib is concerned by this issue.
I will investigate later.
 

Informatix

Expert
Licensed User
Longtime User
I reviewed my code and saw a solution for your issue.

Gesture Detector was designed to add the detection of new gestures, not to prevent the detection of gestures already supported. In a ListView, for example, you can use GD to detect a double tap, not to block an item click. However, and theoretically, returning True in the OnTouch event should indicate that the event is handled at the GD level and the event should be consumed so the view or any other view below should not see it; False, on the contrary, should mean that the event is ignored by GD and, depending on what the view decides, it should be handled by the view or by any other view below. But that's theory and you saw that some events are intercepted directly by the view in a few rare cases. Explaining why is a bit long and won't help. Typically a Panel will behave always as expected while a Listview or a Scrollview won't.

What I can do for you is to add to my library a way to cancel all subsequent events. A IgnoreEvents flag, for example, would cancel all touch events raised for the specified view. That would do what you expect. What do you think?
 

Informatix

Expert
Licensed User
Longtime User
As I just tested, it's perfect! Thank you very much!
I posted the new version in post #1. I decided to make things simple: now returning True or False in the onTouch event works exactly as expected in theory. So you can consume all touch events by returning True (only GD can still see them and react to them). I hope that won't break something somewhere.
 

AndrewKing

Member
Licensed User
Longtime User
I tested GD v2.3 in post #1 with Listview & ScrollView2D, found no problem. The best news is - there is no need to add or change any code. It makes things as simple as possible.
 

mmucek

Member
Licensed User
Longtime User
??? onFling is a directional gesture. It cannot give you an item index or anything related to a click. When the finger is up and the scrolling is finished, what item should be considered?
I just want sweep item to remove it from listView. Have to know position to remove it.
 
Top