i have problem with onTouch. I have GridLayout where i have 3x3 buttons like a image below.
B1,B2 -- is the Button view. My problem is, when i put 3 fingers on the screen at the same time and move all of them down then my phone detect it like threeSwipeGesture and make screenshot. How to avoid phone to detect gesture?
TIP: When i put 3 fingers one by one (tap 1 - wait second - tap 2 - wait second - tap 3) then i can move my fingers and gesture is not detected.
#Override
public boolean onTouch(View view, MotionEvent event) {
int action = event.getActionMasked();
int index = event.getActionIndex();
int x = -1;
int y = -1;
switch (action) {
case MotionEvent.ACTION_MOVE:
return false;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_CANCEL:
gridLayout.setBackgroundColor(Color.parseColor("#df4930"));
set.clear();
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
x = (int) event.getX(index);
y = (int) event.getY(index);
for (int i = 0; i < gridLayout.getChildCount(); i++) {
View button = gridLayout.getChildAt(i);
Rect outRect = new Rect(button.getLeft(), button.getTop(), button.getRight(), button.getBottom());
if (outRect.contains(x, y)) {
set.add(button.getTag());
}
}
if (set.size() == 4) {
Log.d(DEBUG_TAG, set.toString());
gridLayout.setBackgroundColor(Color.parseColor("#ffc120"));
}
}
return true;
}
Here is error log in the console when gesture is detected.
D/ViewRootImpl: cancle motionEvent because of threeGesture detecting
Related
After doing some tutorials on touch events and drawing on a canvas I tried to combine what I learned and make an application that creates a rectangle using 2 touch points. I am able to drag and create a rectangle using a single touch point but as soon as I use a second finger the rectangle starts behaving weirdly, either not drawing the rectangle at all or disappearing after dragging it a bit. Furthermore, if for example I draw a rectangle by having one touch point being on the lower left corner and the other one on the upper right corner of the screen the rectangle disappears if I drag my fingers to the point where they cross each other.
gif of the application running
private float xDown = 0,yDown = 0, xUp = 0, yUp = 0;
boolean touched = false;
#Override
protected void onDraw (Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
if(touched) {
canvas.drawRect(xDown, yDown, xUp, yUp, mPaint);
}
}
#Override
public boolean onTouchEvent (MotionEvent event) {
int fingers = event.getPointerCount();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
if (fingers == 1) {
xDown = event.getX(0);
yDown = event.getY(0);
xUp = 0;
yUp = 0;
}
if (fingers == 2) {
xUp = event.getX(1);
yUp = event.getY(1);
xDown = event.getX(0);
yDown = event.getY(0);
touched = true;
}
break;
case MotionEvent.ACTION_MOVE:
if (fingers == 1) {
xUp = event.getX();
yUp = event.getY();
touched = true;
}
if (fingers == 2) {
xUp = event.getX(1);
yUp = event.getY(1);
xDown = event.getX(0);
yDown = event.getY(0);
touched = true;
}
break;
case MotionEvent.ACTION_UP:
if (fingers == 1) {
xUp = event.getX();
yUp = event.getY();
touched = true;
}
if (fingers == 2) {
xUp = event.getX(1);
yUp = event.getY(1);
xDown = event.getX(0);
yDown = event.getY(0);
touched = true;
}
break;
}
invalidate();
return true;
MotionEvent.ACTION_DOWN will only happen with the FIRST touch event to the screen.
"ACTION_DOWN
Constant for getActionMasked(): A pressed gesture has started..." (From https://developer.android.com/reference/android/view/MotionEvent#ACTION_DOWN)
For your SECOND or more fingers, you need to check for ACTION_POINTER_DOWN
"ACTION_POINTER_DOWN
Constant for getActionMasked(): A non-primary pointer has gone down..." (From https://developer.android.com/reference/android/view/MotionEvent#ACTION_POINTER_DOWN)
Because you are using only two fingers, you won't need to worry about the ActionIndex ( getActionIndex() ). The first finger touching the screen is always an ACTION_DOWN and every touch down after is an ACTION_POINTER_DOWN.
On the ACTION_UP it is the reverse. Every touch up (except the last touch) is ACTION_POINTER_UP and the last touch up is an ACTION_UP.
NOTE: The first touch (ACTION_DOWN) always has and index of 0.
The index stays assigned to the touch until the touch is removed from the screen. Because you are hard-coding the indexes, any accidental touches to the screen will cause some unexpected results. For a test program, this is fine but you will need to handle the indexes eventually.
Example:
Finger 1 (ACTION_DOWN) touches the screen and gets an index of 0.
Finger 2 (ACTION_POINTER_DOWN) touches the screen and gets an index of 1.
Finger 3 (ACTION_POINTER_DOWN) touches the screen and gets an index of 2
Finger 2 (index 1) is lifted off the screen (ACTION_POINTER_UP)
Finger 1 (index 0) and finger 3 (index 2) are still in the MotionEvent and their indexes remain assigned to them.
The simplest change for you to make is Change:
case MotionEvent.ACTION_DOWN:
if (fingers == 1) {
...
to:
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
if (fingers == 1) {
...
and also Change:
case MotionEvent.ACTION_UP:
if (fingers == 1) {
...
to:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
if (fingers == 1) {
...
because you are already handling the finger count & indexes.
This change will make you code "more correct" because it will capture the second DOWN and the first UP but it will not fix your issue. ACTION_MOVE is already catching both touch points when they move and you are already handling the two indexes and assigning the X's & Y's. So the issue has to be elsewhere in you program.
So I've been in this for a while now and have been unable to figure out how to implement 4-way swipe recognition in android. I've found different links pointing to different solutions but none of them fulfill my requirements.
What I wish to accomplish is to attain a "Badoo" like swipe gesture. Badoo is a Tinder like application. On swiping up/down the user's pictures are scrolled, and on left/right, the card is liked/disliked.
The animation I wish to achieve is that on left/right the card follows the set left/right path and moves to like/dislike. On up/down the pictures snap to their next/previous ones.
I've tried countless libraries over github that for swipeable cards and am now trying to implement a custom view for my requirements.
Currently I'm trying to insert a VerticalViewPager inside a CardView. The problem here is that the VerticalViewPager is intercepting all touch events therefore I am unable to swipe my card right/left.
This is my OnTouch event for the CardView item:
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
pager.requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
pager.requestDisallowInterceptTouchEvent(false);
break;
}
return true;
}
This is what I'm trying to achieve. This is what Badoo looks like:
Badoo Screenshot 1: https://drive.google.com/open?id=0BzGcu10X5GRrTDZHRm4xT0tnQ1k
Badoo Screenshot 2: https://drive.google.com/open?id=0BzGcu10X5GRrc2pvRTZKS2p2UEU
If anyone knows of any similar libraries with a similar 4-way swipe gesture please let me know.
EDIT:
I've made a parent view for the card and the ViewPager, the CardView (cardViewParent) has a child cardView and cardView's child is a VerticalViewPager (mViewPager):
cardViewParent.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
Toast.makeText(context, "incardviewPARENT", Toast.LENGTH_SHORT).show ();
float rawX = 0, rawY = 0;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//gesture has begun
float x;
float y;
//cancel any current animations
mActivePointerId[0] = event.getPointerId(0);
x = event.getX();
y = event.getY();
rawX = event.getRawX();
rawY = event.getRawY();
initialXPress[0] = x;
initialYPress[0] = y;
initialRawXPress[0] = rawX;
initialRawYPress[0] = rawY;
break;
case MotionEvent.ACTION_MOVE:
//gesture is in progress
final int pointerIndex = event.findPointerIndex(mActivePointerId[0]);
//Log.i("pointer index: " , Integer.toString(pointerIndex));
if (pointerIndex < 0 || pointerIndex > 0) {
break;
}
final float xMove = event.getX(pointerIndex);
final float yMove = event.getY(pointerIndex);
float rawDY = Math.abs(event.getRawY() -
initialRawYPress[0]);
float rawDX = Math.abs(event.getRawX() - initialRawXPress[0]);
//calculate distance moved
final float dx = xMove - initialXPress[0];
final float dy = yMove - initialYPress[0];
Log.d("RAW DY:", "" + rawDY);
if(rawDY > 100 && !animationStart[0])
{
// vertical swipe detected
mViewPager.setSwipeable(true);
cardStack.setSWIPE_ENABLED(false);
// mViewPager.dispatchTouchEvent(event);
return false;
}
if((rawDY <100 && rawDX > 100) ||
animationStart[0]) {
//horizontal swipe
animationStart[0] = true;
cardStack.setSWIPE_ENABLED(true);
mViewPager.setSwipeable(false);
// cardStack.dispatchTouchEvent(event);
return false;
}
}
return true;
}
});
If an Upwards Swipe Motion is detected then I'm enabling the ViewPager's swipe motion, else the CardStack's (swipe deck) swipe is enabled. The problem is that the swipe motion doesn't start in any direction.
I'm using motion event, action_down, action_move etc, to detect when there is no finger movement but the finger is still on the screen. For instance, the user moves in a vertical line from top of the screen to the bottom and then stops movement without lifting the finger. How do I detect when there is no movement but the finger is still on the screen after the drag/swipe?
EDIT: What I'm trying to do is count every time I change direction of movement in a vertical direction. And to do that I'm trying to detect when I stop movement against the change of movement. For instance, I move down the screen and then move back up, that counts as two counts. Here is my code, please don't provide me with code as a direct answer but hints or clues so that I can try and figure it out myself (my code might look a bit confusing, I'm just trying out different things):
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
oldX = event.getX();
oldY = event.getY();
oldSpeedY = System.currentTimeMillis();
break;
case MotionEvent.ACTION_MOVE:
posY = event.getY();
posSpeedY = System.currentTimeMillis();
float timeElapsed = (posSpeedY - oldSpeedY) / 1000;
float diffSpeed = posSpeedY - oldSpeedY;
if (changeOfMovement(posY, oldY)) {
//if (diffSpeed == 0)
count += 1;
}
break;
case MotionEvent.ACTION_UP:
// count seconds here
break;
}
Toast.makeText(getApplicationContext(), "Swipe: " + count,
Toast.LENGTH_SHORT).show();
return false;
}
public boolean changeOfMovement(float posY, float oldY) {
int newY = Math.round(posY);
double distance = Math.abs(newY - oldY);
oldY = newY;
//float speed = (float) (distance / time);
//if (distance < 25)
//return false;
//if (speed == 0)
//return true;
if (distance < 25)
return true;
return false;
}
The finger touches the screen until you receive either of the MotionEvent actions ACTION_UP or ACTION_CANCEL
The ACTION_DOWN Event is still valid until the finger is lifted from the screen or you can wait till a ACTION_UP Event is detected
I'm not sure if my situation is like yours, but mine I was want to detect if the user stopped moving inside circle within one second and starts moving again, by comparing between last two currentTimeMillis.
So, what I've done is I initialized fixed ArrayList to save the last two times in move event:
public class FixedArrayList extends ArrayList {
int fixedSize = 10;
public FixedArrayList(){}
public FixedArrayList(int fixedSize){
this.fixedSize = fixedSize;
}
#SuppressWarnings("All")
public void addItem(Object object){
if(size() < fixedSize){
add(object);
} else{
remove(0);
add(object);
}
}
}
Now, I've initialized my new class with fixed 2 items to be saved:
FixedArrayList savedLastMove = new FixedArrayList(2);
int secondsToWait = 1;
public boolean onTouchEvent(MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case (MotionEvent.ACTION_MOVE):
currentSystemTime = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
savedLastMove.addItem(currentSystemTime);
if(savedLastMove.size() >= 2 && ((long)savedLastMove.get(1) - (long)savedLastMove.get(0)) >= secondsToWait){
//Do what you want after secondsToWait
}
return true;
}
I hope that will help! Because it solved my problem.
i am trying to do finger swipe but it is not detected .i am trying to draw a path which describes the area through which finger swipe but only when i swipe with force then only swipe get detected and else not detected
public boolean onTouchEvent(MotionEvent event) {
if (System.currentTimeMillis() - lastClick > 500) {
lastClick = System.currentTimeMillis();
synchronized (getHolder()) {
int eventaction = event.getAction();
if(event.getPressure()>0.00001||event.getSize()>0.00001)
{
float a,b,c;
switch(eventaction)
{
case MotionEvent.ACTION_DOWN:
xdown = event.getX();
ydown =event.getY();
pres=true;
break;
case MotionEvent.ACTION_MOVE:
if(pres)
{
xmove = event.getX();
ymove =event.getY();
a = (float) Math.sqrt(Math.pow(xdown - xmove,2) +Math.pow( ydown - ymove,2));
b=(float) (xdown-(((xdown - xmove) / a) * 150));
c=(float) (ydown-((( ydown - ymove)/a)*150));
move.moveTo(xdown, ydown);
move.lineTo(b, c);
pres=false;
lmove=true;
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_POINTER_UP:
move.reset();
xmove = 0;
ymove =0;
xdown=0;
ydown=0;
lmove=false;
pres=false;
break;
default:
return true;
}
}
}}
return true;
}
Android has a class for that. Check http://developer.android.com/reference/android/view/GestureDetector.html
I believe it is one of in touch events
See the code here: Simple swipe gesture to activity tutorial?
Using a SimpleOnGestureListener is easiest, and if you override the onFling method, it's easy/
i am a student developing an air hockey android games.
i am having a problem with understanding multi touch.
i just learns about ACTION_DOWN, ACTION_POINTER_DOWN etc.
but by problem is at ACTION_MOVE.
i create 2 sprite for 2 player.1st sprite will move where my 1st finger go, but my 2nd sprite doesn't move where my 2nd finger move.
my question is, how i want to identified which finger is moving in ACTION_MOVE? i have tried to use getPointerId(index), but i am not understand how to use it because the index is changing if the 1st finger leave the screen
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
break;
}
case MotionEvent.ACTION_POINTER_DOWN: {
break;
}
case MotionEvent.ACTION_UP: {
break;
}
case MotionEvent.ACTION_POINTER_UP: {
break;
}
case MotionEvent.ACTION_MOVE: {
if((int)event.getPointerId(index) == 0){ //i know this IF statement is wrong, what should i do?
player1.setX((int)event.getX()); //player1 & player2 is a sprite object
player1.setY((int)event.getY());
}
if((int)event.getPointerId(index) == 1){
player1.setX((int)event.getX());
player1.setY((int)event.getY());
}
}
}
You cannot know which area will be touched first. The first finger/ touch gets pointer id = 0 the second = 1 and so on. What you can do is this:
public boolean onTouchEvent(MotionEvent event) {
// If this does not work search for a way to get the screen width
WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int eventaction = event.getActionMasked();
int num = event.getPointerCount();
// For every touch
for (int a = 0; a < num; a++) {
int X = (int) event.getX(event.getPointerId(a));
int Y = (int) event.getY(event.getPointerId(a));
int allowed_touch_range = display.getWidth() / 2; // Your screen width divided by 2
switch (eventaction) {
case MotionEvent.ACTION_MOVE:
// Left half of the screen
if (X < allowed_touch_range) {
/* Player1 Move */
}
// Rigth half
if (X > allowed_touch_range) {
/* Player2 Move */
}
break;
}
}
return true;
}