Android: catch MotionEvent across activities - android

I have two activities A and B. I would like to have one touch event MotionEvent.ACTION_DOWN caught in A, while still holding down, launch B, then having the release event MotionEvent.ACTION_UP be caught in B.
In A there is a View v that has an OnTouchListener with the following callback:
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
startActivity(new Intent(A.this, B.class));
break;
case MotionEvent.ACTION_UP:
// not called
break;
}
// false doesn't work either
return true;
}
In B there is an overlapping View v2 (over the original v) with the same kind of OnTouchListener but B's "onTouch" is not getting called when activity starts unless I move my finger (regenerating touch events).
Simply put, I am doing an application that would cause a new activity to appear when holding down on the screen and finish when I release the finger.
Is it not possible to have a MotionEvent.ACTION_DOWNed state transfer from one view to another? Or does the new activity B clear any current "on screen touch listeners" only available to A because it was initiated there?
Thanks for any explanation of how these MotionEvents get dispatched to activities and/or any solution/hack to my problem.

#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
startActivity(new Intent(A.this, B.class));
break;
case MotionEvent.ACTION_UP:
// Obtain MotionEvent object
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis() + 100;
float x = 0.0f;
float y = 0.0f;
// List of meta states found here: developer.android.com/reference/android/view/KeyEvent.html#getMetaState()
int metaState = 0;
MotionEvent motionEvent = MotionEvent.obtain(
downTime,
eventTime,
MotionEvent.ACTION_UP,
x,
y,
metaState
);
// Dispatch touch event to activity (make B static or get the activity var some other way)
B.OnTouchEvent(motionEvent);
break;
}
// false doesn't work either
return true;
}
And in B Activity Override OnTouchEvent (make B implements OnTouchListener) like this:
#Override
public bool OnTouchEvent( MotionEvent e )
{
return someview.OnTouchEvent( e );
}
And remember , someview must be a view in order to catch the ontouchevent , cause the activitys doesn't really knows what to do with it.

Related

How to implement swipe and hold gesture in Android?

I am trying to implement a "swipe and hold" gesture in Android where the user swipes from a point on the left to another point on the right, then continues to hold at the right point until a server-side action is complete. The idea being that if the user lets go of the hold, the server-side action will be cancelled.
I've been trying to go about this the following way but have encountered an issue when getting to the hold part of the gesture. What I'd like to have happen is that when the user reaches point B on the right, the ACTION_MOVE is somehow converted into an ACTION DOWN for the view on the right and from then on, as long as the event ACTION_DOWN continues the gesture is valid, but if the user lets go or leaves the view area, it is invalid. So the question is, is it possible to chain the ACTION_MOVE from left to right into a "hold" gesture once the user reaches point B on the right? I've tried adding an onTouch to point B after the swipe, but the only way that works is if the user lets go of the swipe, then presses point B again. Will this be possible, or should I go about this a different way, such as drag and drop?
Point A and B as found below are currently image views, but that's more placeholder than anything.
private boolean mValidGesture = false;
Rect outRect = new Rect();
int[] location = new int[2];
private boolean isViewInBounds(View view, int x, int y){
view.getDrawingRect(outRect);
view.getLocationOnScreen(location);
outRect.offset(location[0], location[1]);
return outRect.contains(x, y);
}
...
mPointA.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int)event.getRawX();
int y = (int)event.getRawY();
if(event.getAction() == MotionEvent.ACTION_MOVE){
if (isViewInBounds(mPointB, x, y)) {
mPointB.dispatchTouchEvent(event);
Log.d(TAG, "onTouch ViewB");
Toast.makeText(MainActivity.this, "Swipe complete", Toast.LENGTH_SHORT).show();
//This is the part that happens only after you let go of the swipe :(
mPointB.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
mValidGesture = true;
Log.d(TAG, "Gesture: " + String.valueOf(mValidGesture));
return true;
case MotionEvent.ACTION_UP:
mValidGesture = false;
Log.d(TAG, "Gesture: " + String.valueOf(mValidGesture));
return true;
}
return false;
}
});
} else if(isViewInBounds(mPointA, x, y)){
Log.d(TAG, "onTouch ViewA");
}
}
// Further touch is not handled
return false;
}
});
You don't need to put touchListener on pointB. Try something like this:
boolean serverActionStarted = false;
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int)event.getRawX();
int y = (int)event.getRawY();
if(event.getAction() == MotionEvent.ACTION_MOVE){
if (isViewInBounds(mPointB, x, y)) {
// TODO : swipe completed. Start server action
serverActionStarted=true;
}
}else if(event.getAction() == MotionEvent.ACTION_UP && serverActionStarted){
// TODO : cancel the server action
serverActionStarted = false;
}
So basically, when the user swipes from pointA to pointB, you start the server action and set a boolean serverActionStarted to true. After that, if ACTION_UP event occurs, with the severAction having started, you cancel the server action.

How to programmatically cancel the OnTouchListener callback even if the user haven't released the touch from a view in Android?

I am working on an Audio Recording App. It works in a way that when the user presses and moves the record button, the button moves along with the finger. I have created a boundary and when the finger crosses that boundary I want the button to perform the hide() animation and get back to it orginal position.
The whole process works fine if the MotionEvent.ACTION_UP or MotionEvent.ACTION_CANCEL event is occurred, but the hide() operation is not occurring even if the touch crosses the boundary. The button plays a back and forth motion sometimes when it is outside the boundary. The touch event is still being called even if I set the visibility of the view to false.
I get the output in the logcat as well (Log.e("MSG","boundary crossed");).
This is the code:
int recordButtonStartX;
microPhoneListner=new View.OnTouchListener() {
#Override
public boolean onTouch(View v, final MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
recordButtonStartX = (int) event.getX();
this.floatingRecordButton.display(event.getX());
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
this.floatingRecordButton.hide(event.getX());
break;
case MotionEvent.ACTION_MOVE:
int tempX = (int) event.getX();
if ((recordButtonStartX - tempX) > 200) {
Log.e("MSG","boundary crossed");
this.floatingRecordButton.hide(event.getX());
}
else
{
this.floatingRecordButton.moveTo(event.getX());
}
break;
}
recordMsgButton.setOnTouchListener(microPhoneListner);
To release the onTouchListener for any View set the listener to null.
recordMsgButton.setOnTouchListener(null);
or
After your condition satisfied you can set other listener to that View.
Make another listener
public final OnTouchListener mTouchListener = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent rawEvent) {
return false;
}
};
When you want to disable the listener then set other listener to that view
v.setOnTouchListener(mTouchListener);

How to get onTouch position on a Button using a top View?

I have a custom View, which inherits from GestureOverlayView, and I want to log all the MotionEvent passed to this view.
It works well, but I can't get the MotionEvent when my gesture starts on an interactive layout widget (Button, TextEdit....)
Is there a way to bypass this behaviour?
Since you didn't mention any snipped code of yours,I make 2 different solution hopefully is help you
One way will be passing your widget to as View to method and call OnTouchListener
button.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
break;
}
case MotionEvent.ACTION_UP: {
break;
}
case MotionEvent.ACTION_CANCEL:{
break;
}
}
return false;
}
});
Another way could be manually creating a MotionEvent using custom constructor
like this:
static public CustomMotionEvent obtain(long downTime, long eventTime, int action,
float x, float y, int State) {
}
According to API
Create a new MotionEvent, filling in a subset of the basic motion
values. Those not specified here are: device id (always 0), pressure
and size (always 1), x and y precision (always 1), and edgeFlags
(always 0).
API

How to detect a TOUCH event with another finger while an ACTION_MOVE is in process

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");
}
}

Delegate touch event from one view to another

I am trying to implement my own drag and drop with touch view events. I want to trigger dragging with long click, in onLongClick i create view shadow as bitmap and this bitmap is set to imageview. This imageview i want to drag. My problem is, that imageview is not responding to touch events immediately after that long click event. I have to stop touching screen and tap to imageview again and then my image is moving.
Some relevant code:
mCategoryNews.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
v.setVisibility(View.INVISIBLE);
ImageView shadow = (ImageView) getView().findViewById(R.id.imgViewShadow);
shadow.setLayoutParams(new FrameLayout.LayoutParams(v.getWidth(), v.getHeight()));
shadow.setImageBitmap(Utils.loadBitmapFromView(v));
shadow.bringToFront();
((FrameLayout.LayoutParams) shadow.getLayoutParams()).leftMargin = rowCategories1.getLeft();
((FrameLayout.LayoutParams) shadow.getLayoutParams()).topMargin = rowCategories1.getTop();
return true;
}
});
private View.OnTouchListener mDragShadowTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG, "onTouch");
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "action move");
int x = (int) event.getRawX();//- rowCategories1.getLeft() - v.getWidth() / 2;
int y = (int) event.getRawY();//- rowCategories1.getTop() - v.getHeight();
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(mRowWidth / 2, mRowHeight);
params.setMargins(x, y, 0, 0);
v.setLayoutParams(params);
break;
case MotionEvent.ACTION_DOWN:
break;
}
return true;
}
};
no log output is present while i am still holding finger on screen after long tap.
This is a late answer, but I had a similar problem and found this solution:
Pass the MotionEvent to the view that should take it over via the dispatchTouchEvent method.
View myView = findViewById(R.id.myView);
#Override
public boolean onTouchEvent(MotionEvent event) {
myView.dispatchTouchEvent(event);
}
After the MotionEvent is passed, myView takes over and responds to new touch events.
If you return true in onLongClick() it means the callback has consumed the event and thus it is not propagated further. If you return false, then it will reach down to the child views. (I assume the touch listener is set on the shadow ImageView).
I believe to delegate try returning false

Categories

Resources