Android - How to handle two finger touch - java

The documentation say this about that:
A gesture starts with a motion event with ACTION_DOWN that provides
the location of the first pointer down. As each additional pointer
that goes down or up, the framework will generate a motion event with
ACTION_POINTER_DOWN or ACTION_POINTER_UP accordingly.
So i have done the override of onTouchEvent function in my activity:
#Override
public boolean onTouchEvent(MotionEvent MEvent)
{
motionaction = MEvent.getAction();
if(motionaction == MotionEvent.ACTION_DOWN)
{
System.out.println("DEBUG MESSAGE POINTER1 " + MEvent.getActionIndex() );
}
if(motionaction == MotionEvent.ACTION_POINTER_DOWN)
{
System.out.println("DEBUG MESSAGE POINTER2 " + MEvent.getActionIndex() );
}
}
Unfortunately the second if is never entered. The activity contains 2 view with 2 OnTouchListener, i know that onTouchEvent is called only if the view of the activity don't consume the event so i tried to return false in the listener and in that way i can recognize only the first finger touch but this avoid the listener to receive the ACTION_UP event and don't allow me to recognize the second finger touch. I also tried to return true in the listener but after manually invoke the onTouchEvent function but this allow me to recognize only the first finger touch too.
What's wrong in my code ?

I believe your code is missing the masking operation like:
switch (motionaction & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
}
This code should be able to check for ACTION_POINTER_DOWN.
Good luck & tell us what happens.
Tommy Kwee

Related

Which component can I use in Android Studio for the purpose of creating a touchpad?

I've been searching for a View/Component in Android Studio that I could use for creating a touchpad. I need the ability to track mouse movements, so that I use that information on the server side to handle it and emulate mouse movements.
What would be the best fit for that purpose?
That would be a ScrollView or a HorizontalScrollView.
Unfortunately, Shishdem's answer didn't satisfy me. What I basically was trying to achieve is to make a prototype of a touchpad for a school project. ScrollView contains elements that are to be scrolled within, and I needed to track finger movements information that is suitable to be handled for moving cursor on PC by my server application.
I found solution here: Track touch and pointer movements
I needed to create an android.view.VelocityTracker and track touch velocity to further send that data to my PC server and simulate mouse movements via java.awt.Robot within onTouchEvent. The component could be anything, a Button in my case.
Here's the working code for the class that handles touch event of a touchpad view:
private VelocityTracker mVelocityTracker = null;
#Override
public boolean onTouchEvent(MotionEvent event) {
int index = event.getActionIndex();
int action = event.getActionMasked();
int pointerId = event.getPointerId(index);
switch(action) {
case MotionEvent.ACTION_DOWN:
if(mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
else {
mVelocityTracker.clear();
}
mVelocityTracker.addMovement(event);
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(event);
mVelocityTracker.computeCurrentVelocity(1000);
// Log velocity of pixels per second
// Those values can be used to be sent to the server
Log.d("Velocity", "X velocity: " + mVelocityTracker.getXVelocity(pointerId));
Log.d("Velocity", "Y velocity: " + mVelocityTracker.getYVelocity(pointerId));
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mVelocityTracker.recycle();
mVelocityTracker = null;
break;
}
return true;
}
In case of crash
Note that in the example of the link above there isn't this line:
mVelocityTracker.recycle();
mVelocityTracker = null; // this line
But in my case, if I didn't assign mVelocityTracker to null every recycle, it would crash with the following errors:
2021-10-05 09:30:21.711 4385-4385/com.example.javaapp E/InputEventReceiver: Exception dispatching input event.
2021-10-05 09:30:21.711 4385-4385/com.example.javaapp E/MessageQueue-JNI: Exception in MessageQueue callback: handleReceiveCallback
2021-10-05 09:30:21.713 4385-4385/com.example.javaapp E/MessageQueue-JNI: java.lang.IllegalStateException: Already in the pool!
at android.util.Pools$SimplePool.release(Pools.java:120)
at android.util.Pools$SynchronizedPool.release(Pools.java:179)
at android.view.VelocityTracker.recycle(VelocityTracker.java:87)
More on that issue: VelocityTracker causes crash on Android 4.4

LWJGL: detecting left/right clicks when getButtonCount returns 0

On the Dell Inspiron 15 3000, the touchpad doesn't have any physical left/right buttons. Instead, it is one giant touchpad that is pressure sensitive. I'm assuming it detects right/left clicks based off of hand position on the trackpad.
In my LWJGL application, I detect mouse button clicks with Mouse.isButtonDown(0). This works fine on computers with a mouse with physical buttons, but doesn't work on touchpads that lack physical buttons. Mouse.getButtonCount() returns 0.
Has anybody had any success in detecting if a mouse button is pressed, should the user be using a trackpad that doesn't have physical buttons?
I think, instead of using
org.lwjgl.input.Mouse
This class could be what you are searching for:
org.lwjgl.input.Controllers
http://legacy.lwjgl.org/javadoc/org/lwjgl/input/Controllers.html
Im not entirely sure though since I only have a mouse and no way to test this with a touchpad.
For those who find this in the future, I did find a fix:
You cannot, should there be no physical buttons, use the Mouse.isButtonDown() method. Instead, you are going to have to read the event buffer. To do so, I wrote my own helper class:
public class Mouse{
private static boolean button_left = false, button_right = false;
public static boolean isButtonDown(int button){
if(button == 0) return button_left;
if(button == 1) return button_right;
return false;
}
public static void update(){
while(org.lwjgl.input.Mouse.next()){
if(org.lwjgl.input.Mouse.getEventButton() == 0) button_left = org.lwjgl.input.Mouse.getEventButtonState();
if(org.lwjgl.input.Mouse.getEventButton() == 1) button_right = org.lwjgl.input.Mouse.getEventButtonState();
}
}
}
The update() method is called every tick, and as such I can get button states using Mouse.isButtonDown(button).

MapActivity Force Closes, Touch Event fires recklessly

I have a MapActivity which has a touch event.
In the touch event:
I get the current geocodes
Put an overlay image on that location.
and set it as the center of the mapview.
This seems to work fine and as expected for a while. But then the application force closes. I think the touch event is being fired like crazy even when nothing's being touched. I know that because I had put a toast set up that shows the longitude and lattitude of the touched position. and it keeps showing up even when nothing's touched.
I cant seem to accurately position the problem, let alone fixing it. Here's the touch event for my mapActivity
private void LocationStuffs(double latitude, double longitude){
itemizedOverlay.clear();
itemizedOverlay.addOverlayItem((int)latitude, (int)longitude, "place");
mapView.invalidate();
mapView.getOverlays().add(itemizedOverlay);
MapController mc = mapView.getController();
mc.setCenter(new GeoPoint((int)latitude, (int)longitude));
mc.setZoom(20);
}
public boolean onTouchEvent(MotionEvent event, MapView mapView){
//---when user lifts his finger---
if (event.getAction() == 1) {
GeoPoint p = mapView.getProjection().fromPixels(
(int) event.getX(),
(int) event.getY());
Toast.makeText(getBaseContext(),
p.getLatitudeE6() / 1E6 + "," +
p.getLongitudeE6() /1E6 ,
Toast.LENGTH_SHORT).show();
latitude = p.getLatitudeE6() / 1E6;
longitude = p.getLongitudeE6() / 1E6;
LocationStuffs(latitude*1E6, longitude*1E6);
}
return false;
}
Change your return statement to return true:
return true;
Basically, what that does is that it tells the system that you have handled the touchevent. Let me know if that solves it.
I believe you have few errors in your code:
1- You need a "return true" statement before you close the "if" statement in the "onTouchEvent" method. This tells the system that the event has been handled and it should be consumed. That's probably the reason for multiple touchs.
2- the "mapView.invalidate();" should be moved to the end of your "LocationStuffs" method, as you want to have you screen redrawn after you have add the new location and not before.
3- You are adding repeatedly the same itemizedOverlay to the mapview. After calling your "LocationStuffs" method 10 times, you will have the same overlay added 10 times to the overlays list. You should add the Itemizedoverlay only once, outside this method (on the onCreate or onResume for example)
good luck

Android - AndEngine "No Longer Touched" method?

Hi I'm using AndEngine in my Android app. I'm wondering if there is a method that detects when an object (in this case an object of AnalogOnScreenControls) goes from being touched to untouched? I want to set a specific command that executes only when someone lets go of the "analog stick" entity. The controls also use float values to detect what position they're in, so it could also be a method for when the values go from some value other than zero to zero, since the variables are set to zero when the controls are idle. Thanks for any help in advance!
There is a flag in the TouchEvent that you can check.
Most times I do something like this:
#Override
public boolean onAreaTouched(final TouchEvent touchEvent, ITouchArea touchArea, float touchAreaLocalX, float touchAreaLocalY) {
switch(touchEvent.getAction()){
case TouchEvent.ACTION_MOVE:{
// do stuff when finger moves
return true; // don't forget to break, or return true directly if the event was handled
}
case TouchEvent.ACTION_DOWN:{
// do stuff, the first time the finger touches the display
return true;
}
case TouchEvent.ACTION_UP:{
// do stuff when the finger goes up again and ends the touch event (your case)
return true;
}
case TouchEvent.ACTION_CANCEL:{
// If the event is somehow canceled - e.g. the finger leaves the display
return true;
}
default:{
// none of the above
return false;
}
}
}
Something like that. If you need more information about the event than theses simple actions, get the MotionEvent with touchEvent.getMotionEvent() and check out the additional options there.
BTW: I prefer to use the return true statement directly instead of a break here, just to make sure that the touch event won't get used otherwise in the app. But you can change that of course.
hope this helps
Christoph
You could create a field (global variable) named isTouched and set it initially to false. Then inside onControlChange() you do this:
if(pValueX == 0 && pValueY == 0 && isTouched){ //means knob has recently been touched
isTouched = false; //set to false so that being idle does not come here
//do your thing here
}
else{
isTouched = true;
//do normal stuff here
}

Android - How to distinguish first pointer and second pointer move in onTouchEvent

I want handle a gesture in my activity. To do this i have override the public boolean onTouchEvent(MotionEvent MEvent) method on my Activity. The content looks like this:
motionaction = MEvent.getAction();
if(motionaction == MotionEvent.ACTION_DOWN)
{
...
return true;
}
if(motionaction == MotionEvent.ACTION_UP)
{
...
return true;
}
if(motionaction == MotionEvent.ACTION_MOVE)
{
...
return true;
}
motionaction = MEvent.getActionMasked();
if(motionaction == MotionEvent.ACTION_POINTER_DOWN)
{
...
return true;
}
if(motionaction == MotionEvent.ACTION_POINTER_UP)
{
...
return true;
}
return true;
The gesture it's the follow:
-finger1 on the screen hold its position (virtually because there is always a little movement)
-finger2 move on the screen. This is the movement that i want to grab.
I can grab the 5 action but the problem it's that when two fingers are on the screen the ACTION_MOVE grabs the movement of both first and second finger. The method MEvent.getActionIndex() don't works for the ACTION_MOVE that is return always 0; The only thing that i can do it's to save the position of the finger1 and to discard the movement near to that point. The result it's not perfect indeed sometime the movement of the finger2 it's "tainted" by the little finger1 movement because though the finger holds it's position on the screen the listener feels each minimal movement.
How can i improve this ?
Referring to the answer of Quintin Balsdon:
I have a similar thing in my code. I save the position of the finger1 in the ACTION_DOWN case, then when the finger2 move i see if the Y coordinate of the move is over the saved finger1 Y coordinate. If so the movement it's referred to the finger2 otherwise is referred to finger1 and i discard that in two finger mode.
If i try to draw a circle on my view in one finger mode i have got something like this:
http://img208.imageshack.us/img208/8113/onefingercircle.jpg
If i try to draw a circle on my view in two finger mode i have got something like this:
http://img256.imageshack.us/img256/6778/twofingercircle.jpg
So in one finger mode it work perfectly but not in two finger mode.
I don't know if it's related to the phone multitouch handler or the touch screen. It can be only hardware related also or my misunderstanding of the API.
This is managed by you as the developer. You are going to have to create some boolean variable to set (to true) when the "DOWN" action takes place and then unset (false) when the "UP" or "MOVE" motionevent occurs. In the code below, I maintain the coordinates when the "DOWN" even happens and make adjustments while the user moves around.
switch (e.Action) //e is a MotionEvent type
{
case MotionEvent.ACTION_DOWN:
{
_prevx = e.getX();
_prevy = e.getY();
}
break;
case MotionEvent.ACTION_MOVE:
{
_xoffset += e.GetX() - _prevx;
_yoffset += e.GetY() - _prevy;
Invalidate();
_prevx = e.GetX();
_prevy = e.GetY();
}
break;
}
If you want to do multi-finger dragging you need to implement ScaleGestureDetector.OnScaleGestureListener (http://developer.android.com/reference/android/view/ScaleGestureDetector.OnScaleGestureListener.html)

Categories