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
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 have tried below to disable two finger touch by putting in my fragment layout but did not workandroid:splitMotionEvents="false"
Also i tried below in manifest: <uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false" /> No help.
If anyone knows how to please let me know. Thanks
gestureOverlayView.setOnTouchListener(new View.OnTouchListnener(){
#Override
public boolean onTouch(View v, MotionEvent e){
// True means the event is ignored by the overlayed views
return e.getPointerCount() > 1 ? true : false;
}
}
You can put a GestureOverlayView the whole screen and only allow the first touch event.
You can refer to this answer already there on Stackoverflow
Disable multi finger touch in my app
Use Below code to check run time multitouch and disable it:
private SparseArray<PointF> mActivePointers= new SparseArray<PointF>();
yourlayoutname.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int pointerIndex = event.getActionIndex();
// get pointer ID
int pointerId = event.getPointerId(pointerIndex);
// get masked (not specific to a pointer) action
int maskedAction = event.getActionMasked();
switch (maskedAction) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN: {
// We have a new pointer. Lets add it to the list of pointers
PointF f = new PointF();
f.x = event.getX(pointerIndex);
f.y = event.getY(pointerIndex);
mActivePointers.put(pointerId, f);
if (mActivePointers.size() >= 2) {
//DO NOTHING
}
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL: {
mActivePointers.remove(pointerId);
break;
}
}
return true;
}
});
I am trying the android motion event. However, I do not know why it cant go to > case MotionEvent.ACTION_MOVE:
Here is my codes.
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
startX = (int)event.getX();
startY = (int)event.getY();
selecting=false;
break;
case MotionEvent.ACTION_MOVE:
selecting=true;
break;
case MotionEvent.ACTION_UP:
if(selecting){
endX = (int)event.getX();
endY = (int)event.getY();
selected=true;
}
selecting=false;
break;
}
}
No matter how I swipe and touch, even if I remove if(selecting) condition in case MotionEvent.ACTION_UP, it always show same output.
start: {498.0, 365.0}
end: {0.0, 0.0}
selecting: false
selected: false
After I change it to
public boolean onTouchEvent(MotionEvent event)
everything is okay. Can anyone explain to me why does it happen?
If possible, help me to solve this problem too. How to get the actual point when I touch on screen?
In order to get the following MotionEvents, you must return true in the onTouchEvent method.
source
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 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;
}