I need to implement both Longclick and Left & right swipe on a list view and get the listitem on which the action was performed. This method seemed really promising.
Problems:
1.ACTION_MOVE is fired only once at the start so the diff is really minimal
2.If i use a default in the switch i get the last location but onClick or onLongClick is never fired. Here is what i tried.. Is it possible to fire a fake ACTION to cause itemClick/itemlongclick to execute.
public boolean onTouch(View v, MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
downX = event.getX();
downY = event.getY();
mSwipeDetected = Action.NONE;
Log.i("MyTags","Down Event");
Log.i("MyTags",String.valueOf(downX)+","+String.valueOf(downY));
return false; // allow other events like Click to be processed
case MotionEvent.ACTION_MOVE:
upX = event.getX();
upY = event.getY();
Log.i("MyTags","Move Event");
Log.i("MyTags",String.valueOf(upX)+","+String.valueOf(upY));
moveEnabled=true;
return false;
case MotionEvent.ACTION_UP:
upX = event.getX();
upY = event.getY();
Log.i("MyTags","UP Event");
Log.i("MyTags",String.valueOf(upX)+","+String.valueOf(upY));
return false;
default:
upX = event.getX();
upY = event.getY();
Log.i("MyTags","Default Event");
Log.i("MyTags",String.valueOf(upX)+","+String.valueOf(upY));
if(moveEnabled)
{
diffX=downX-upX;
diffY=downY-upY;
abs_X=Math.abs(diffX);
abs_Y=Math.abs(diffY);
moveEnabled=false;
if((abs_X>abs_Y)&(abs_X>MINIMUM_X))
{
if(diffX>0)
{
mSwipeDetected=Action.LEFT;
Log.i("MyTags","Left Swipe");
event.setAction(MotionEvent.ACTION_UP);
return false;
}
else if(diffX<0)
{
mSwipeDetected=Action.RIGHT;
Log.i("MyTags","Right Swipe");
event.setAction(MotionEvent.ACTION_UP);
return false;
}
}
}
return false;
}
}
I kind of used a hack to fix my issue.What happens is since the events are consumed, i never get the result in the onitemclick event. What i have done is to fire my own Down and Up events once a swipe action is detected and during those fake events i return false so that its passed down to the itemclick listener. We need to be a bit careful here as the event seems to read from the result 2 times , for this i turn the listener on and off , there by containing the output.
ACTION_MOVE:
//when swipe is detected fire a fake down event
event.setAction(MotionEvent.ACTION_DOWN);
v.dispatchTouchEvent(event);
ACTION_DOWN:
if(fake down event)
//set some flags & dispatch fake event
event.setAction(MotionEvent.ACTION_UP);
v.dispatchTouchEvent(event);
return false
else do regular handling
ACTION_UP:
if fake down event
dispatch fake up event
else ..
here 'v' is the view on which the event has to be fired , in my case its the listview.
Since we dont have it right-off it has to be passed in through the constructor of the swipeDetector class. As follows..
ListView v;
SwipeDetector(ListView lv)
{
this.v=lv;
}
Related
At first i always get the ACTION_DOWN mesage so i googled and i found that i have to return TRUE at the end. So i changed it and i started to get always ACTION_UP. I dont understand why.
linearLayoutDraggable.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
//ConstraintLayout.LayoutParams par = (ConstraintLayout.LayoutParams)v.getLayoutParams();
CardView.LayoutParams par = (CardView.LayoutParams)v.getLayoutParams();
switch (event.getAction()) {
case MotionEvent.ACTION_UP: {
//par.height = 40;
Toast.makeText(MagMainNewActivity.this, "UP", Toast.LENGTH_SHORT).show();
// par.height=300;
// par.topMargin = (int) event.getRawY() - (v.getHeight());
// par.leftMargin = (int) event.getRawX() - (v.getWidth() / 2);
// v.setLayoutParams(par);
break;
} //inner case UP
case MotionEvent.ACTION_DOWN: {
Toast.makeText(MagMainNewActivity.this, "DOWN", Toast.LENGTH_SHORT).show();
// par.height = 115;
// //par.width = 60;
// v.setLayoutParams(par);
break;
} //inner case UP
} //inner switch
return true;
}
});
The onTouchListener receive motion gestures, so (while returning true) if you receive a ACTION_DOWN, you will receive each ACTION_MOVE finalized with only ONE ACTION_UP that means the pointer (finger) was removed from the screen at that point.
A click action is represented from a ACTION_DOWN plus a ACTION_UP, while a ACTION_DOWN without a UP is a long press.
Maybe you are seeing just the UP message from the toast since both events are trigged sequentially and the second toast overrided the first one, if you add Log.v("MMNA", "DOWN") you can check both at the Logcat
Because before finger is up finger down is first, this means MotionEvent.ACTION_DOWN is came frist. It check event action and if finger touch a screen action is MotionEvent.ACTION_DOWN and it break. Sorry for bad English
I am developing a game and I need to be able to detect that one finger is performing a MOVE while posibly another finger can TOUCH another part of the screen.
With the following code I am able to detect both the ACTION_MOVE (on certain region of the screen) and the ACTION_DOWN
public boolean onTouch(View v, MotionEvent event) {
final int dest_x = (int) event.getX();
final int dest_y = (int) event.getY();
onTrackPad = dbSettings.TRACK_PAD.contains(dest_x, dest_y);
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (onTrackPad)
{
//move character
}
break;
case MotionEvent.ACTION_DOWN:
// Fire bullets
break;
}
//The event was consumed
return true;
}
The problem is that I am not able to move and fire at the same time (I need to stop moving in order to fire and viceversa)
I am aware that Android can handle multi-touch events but have not figure it how to use that to be able to process these events and the same time so that the player can move and fire at the same time
I have also try using the getActionMasked without any luck
After reading this question Android MotionEvent.getActionIndex() and MultiTouch
This is how I solved the problem
public boolean onTouch(View v, MotionEvent event) {
int dest_x ;
int dest_y ;
p = event.getActionIndex() ;
dest_x = (int) event.getX(p);
dest_y = (int) event.getY(p);
onTrackPad = dbSettings.TRACK_PAD.contains(dest_x, dest_y);
action = event.getActionMasked() ;
switch (action) {
case MotionEvent.ACTION_MOVE:
if (onTrackPad)
{
//move character
}
break;
case MotionEvent.ACTION_DOWN:
// Fire bullets
break;
}
//The event was consumed
return true;
}
MotionEvent has all information about touches that you need. You can get number of touches by executing event.getPointersCount(), and try to check MotionEvent.ACTION_POINTER_2_DOWN instead of MotionEvent.ACTION_DOWN. To get coordinates of each touch you can use event.getX(0) and event.getX(1), same is for y. If you have a case of MotionEvent.ACTION_MOVE with 2 touches, you will receive all this information in your motion event.
Try below code.
when multiple pointers touches on the screen,system generates the action events.we can keep track of individual pointers with in motion event using pointer id. Pointer id persists across touch events and also allows to track individual pointer across entire gesture.
#Override
public boolean onTouch(View v, MotionEvent event) {
int index = event.getActionIndex();
int pointerID = event.getPointerId(index);
int action = event.getActionMasked();
if(event.getPointerCount() > 1)
{
Log.i("TouchType ", "Multi Touch");
for(int i = 0; i < event.getPointerCount(); i++)
{
performAction(action);
}
}else
{
Log.i("TouchType ", "Single Touch");
performAction(action);
}
return true;
}
public void performAction(int action){
switch(action)
{
case MotionEvent.ACTION_DOWN :
Log.i("OnTouch ", "Pressed");
// Fire bullets
break;
case MotionEvent.ACTION_MOVE :
Log.i("OnTiouch", "move");
//move character
break;
case MotionEvent.ACTION_UP :
Log.i("OnTiouch", "Up");
break;
default:
Log.i("OnTiouch", "None");
}
}
I want to catch ONLY two fingers touch (one finger touch must be NOT consumed).
Unfortunately, I need to consume the "one finger touch" in order to get the "two finger touch" events.
I hope I'm a clear.
Here my code :
#Override
public boolean onTouch(View v, MotionEvent m)
{
boolean consumed = false;
int pointerCount = m.getPointerCount();
int action = m.getActionMasked();
if(pointerCount == 2)
{
switch (action)
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
consumed = true;
break;
case MotionEvent.ACTION_UP:
// Work
break;
case MotionEvent.ACTION_MOVE:
// Some stuff
break;
default:
break;
}
}
else if (pointerCount == 1 && action == MotionEvent.ACTION_DOWN)
{
// This is needed to get the 2 fingers touch events...
consumed = true;
}
return consumed;
}
I've ran into a similar problem and solved my problem by handling all touches at my top view, and propagating the appropriate actions to the views below. In my example, I had an overlay layer that needed to intercept scale gestures (a recognizer is attached in my constructor), while still propagating single touches to my map view below. I haven't sent the actual touch event to my map view, but I've handled it in my top view and sent the appropriate "panning" action to my view below.
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getPointerCount() == 2) {
if(event.getActionMasked() == MotionEvent.ACTION_DOWN){
CameraUpdate update = CameraUpdateFactory.newLatLng(initialCoords);
//attachedMapFragment.getMap().moveCamera(update);
}
scaleGestureDetector.onTouchEvent(event);
}else{
if(attachedMapFragment != null) {
if(event.getActionMasked() == MotionEvent.ACTION_DOWN){
initialCoords = attachedMapFragment.getMap().getCameraPosition().target;
lastTouchX = event.getX();
lastTouchY = event.getY();
}else if(event.getActionMasked() == MotionEvent.ACTION_MOVE){
float deltaX = event.getX() - lastTouchX;
float deltaY = event.getY() - lastTouchY;
lastTouchX = event.getX();
lastTouchY = event.getY();
Projection projection = attachedMapFragment.getMap().getProjection();
Point center = projection.toScreenLocation(attachedMapFragment.getMap().getCameraPosition().target);
Point newCenter = new Point(center.x - (int)deltaX, center.y - (int)deltaY);
LatLng newCoords = projection.fromScreenLocation(newCenter);
CameraUpdate update = CameraUpdateFactory.newLatLng(newCoords);
attachedMapFragment.getMap().moveCamera(update);
}
}
}
return true;
}
This way, I had a scale recognizer on my custom view, while the map was still panning appropriately to single touch. Actually, map view wasn't getting any touches at all, my custom view was "routing" the action as seen above. While not always ideal, this would solve the problem in many cases.
My problem is that when I try to drag an item in my ListView, I don't always get the ACTION_DOWN event. I got lots of ACTION_MOVE events and only one ACTION_UP event.
It is not always the case. I got ACTION_DOWN 3 times. It confused me.
I looked at similar questions but it's answers seems not fit to mine . Can anyone think of why this is happening?
Thanks
//list_client -- a listview
list_client.setOnTouchListener(new View.OnTouchListener() {
float f1 = -1, f2 = -1 ;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
f1 = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
f2 = event.getRawY();
if(f2 - f1 > 50){
if(View.VISIBLE != rl_search_and_add.getVisbility() && ){ rl_search_and_add.setVisibility(View.VISIBLE);
}
f1 = f2;
}else if (f2 - f1 < -50){
rl_search_and_add.setVisibility(View.GONE);
f1 = f2;
}
break;
case MotionEvent.ACTION_UP:
f1 = -1; f2 = -1;
break;
}
return false;
}
});
There could be several reasons for that behavior.
It is possible that the user missed the list a bit and the MotionEvent.ACTION_DOWN been handled by other component, but how ever the user continue dragging and hit your list view area, you received MotionEvent.ACTION_MOVE actions.
Another possibility is that, you had to many events, and they been moved to historical event, and you only received the latest event. You can use all the historical methods of MotionEvent to see them.
you should override onInterceptTouchEvent like this:
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
yDown = event.getRawY();
break;
default:
break;
}
return super.onInterceptTouchEvent(event);
}
then using yDown to your program
I have an Android applciation which I want it to draw circles around
I used OnTouchListener.
The problem is, when the users "holds" the circle in place, it doesn't update the action.
How can I know if the user's finger is on,when he doesn't move it, with the OnTouchListener
You'll have to check if what kind of event is triggering onTouchEvent.
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//Do Nothing
return true;
case MotionEvent.ACTION_MOVE:
//Do Something
path.lineTo(eventX, eventY);
break;
case MotionEvent.ACTION_UP:
//Do Nothing
break;
default:
return false;
}
// Schedules a repaint.
invalidate();
return true;
}
More Information about MotionEvents can be found here.
You can know that by using
MotionEvents:
Reed more here: MotionEvents
ACTION_DOWN is for the first finger that touches the screen. This starts the gesture. The pointer data for this finger is always at index 0 in the MotionEvent.
ACTION_POINTER_DOWN is for extra fingers that enter the screen beyond the first. The pointer data for this finger is at the index returned by getActionIndex().
ACTION_POINTER_UP is sent when a finger leaves the screen but at least one finger is still touching it. The last data sample about the finger that went up is at the index returned by getActionIndex().
ACTION_UP is sent when the last finger leaves the screen. The last data sample about the finger that went up is at index 0. This ends the gesture.
ACTION_CANCEL means the entire gesture was aborted for some reason. This ends the gesture.
here is a good answer to read StackOverFlow
Filter touches by what kind they are:
boolean onScreen = false;
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX(); //X coord of the touch
float eventY = event.getY(); //Y coord of the touch
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// User has touched the screen
onScreen = true;
return true;
case MotionEvent.ACTION_MOVE:
// Finger is being dragged on the screen
onScreen = true; //Not required, just for clarity purposes.
break;
case MotionEvent.ACTION_UP:
// User has lifter finger
onScreen = false;
break;
default:
return false;
}
if(onScreen) {
//Finger is on the screen
}
return true;
}