So I think I have a very simple issue but I can't seem to figure it out.
I have an ImageView and I am using it's setOnTouchListener method (OnTouch).
How can I differentiate between the ACTION_DOWN event and ACTION_MOVE event?
Even when I just click (touch) on the ImageView, ACTION_MOVE event gets called.
My goal is to open something(do anything) when the user clicks on it and move it when user holds it and moves it.
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
initialX = params.x;
initialY = params.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
return true;
case MotionEvent.ACTION_UP:
return true;
case MotionEvent.ACTION_MOVE:
params.x = initialX + (int) (event.getRawX() - initialTouchX);
params.y = initialY + (int) (event.getRawY() - initialTouchY);
mWindowManager.updateViewLayout(mImgFloatingView, params);
// Log.d("Params", "X: " + params.x + ". Y: " + params.y + ".");
if(params.x == initialX && params.y == initialY) {
Toast.makeText(getBaseContext(), "Test", Toast.LENGTH_SHORT).show();
}
return true;
}
return false;
}
As others have said, ACTION_MOVE is called along with ACTION_DOWN because of the sensitivity of the device and the inherent unsensitivity of big fingers. This is known as touch slop. The results you want can be obtained by adjusting the thresholds for time and distance moved. Alternatively, you could use a gesture detector.
OnTouch MotionEvent Actions
Based on the title of the question, I came here looking for a quick reference for the onTouch MotionEvent actions. So here is a snippet copied from the documentation:
#Override
public boolean onTouchEvent(MotionEvent event){
int action = event.getActionMasked();
switch(action) {
case (MotionEvent.ACTION_DOWN) :
Log.d(DEBUG_TAG,"Action was DOWN");
return true;
case (MotionEvent.ACTION_MOVE) :
Log.d(DEBUG_TAG,"Action was MOVE");
return true;
case (MotionEvent.ACTION_UP) :
Log.d(DEBUG_TAG,"Action was UP");
return true;
case (MotionEvent.ACTION_CANCEL) :
Log.d(DEBUG_TAG,"Action was CANCEL");
return true;
case (MotionEvent.ACTION_OUTSIDE) :
Log.d(DEBUG_TAG,"Movement occurred outside bounds " +
"of current screen element");
return true;
default :
return super.onTouchEvent(event);
}
}
Notes:
The above code could be used in an activity or a subclassed view. If subclassing a view is not desired, then the same motion events can be tracked by adding an OnTouchListener to the view. (example also taken from the documentation)
View myView = findViewById(R.id.my_view);
myView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// ... Respond to touch events
return true;
}
});
Difference between getAction() and getActionMasked()
Returning true means that future events will still be processed. So if you returned false for ACTION_DOWN then all other events (like move or up) would be ignored.
Further reading
Using Touch Gestures: This is a series of official lessons in the documentation. It is an essential read for understanding how to correctly handle touch events and gestures.
What is happening is that View.OnTouchListener will send you ALL events. If you tap and move by only 1 pixel, it'll be registered as a ACTION_MOVE.
You will need to implement some logic to determine a 'hold' action - waiting 100ms or something similar - after which a 'drag' action can proceed. Before that time, it should just be a 'click' action.
Related
I have this code for my MotionEvents in Android:
#Override
public boolean onTouchEvent(MotionEvent event){
int action = event.getActionMasked();
switch (action){
case MotionEvent.ACTION_UP:
mGameview.moveUp(30);
break;
case MotionEvent.ACTION_DOWN:
mGameview.moveDown(30);
break;
}
return true;
}
However, instead of these things only triggering when I drag my finger up/down, they both happen simultaneously no matter what I do in the app, for example when I tap, drag left and right.
Try to get action by:
int action = event.getAction() & MotionEvent.ACTION_MASK;
In our android program we are having trouble getting a method to call at the right time. We have an ontouchlistener and we tried to have it called on release, meaning the other functions would proceed it. However, it appears everything in "ontouch" happens at the same time. How do we get this other method to be called after the others have completed?
#Override
public boolean onTouch(View v, MotionEvent event) {
Tile touchedTile = (Tile) v;
if (touchedTile.isEmpty() || !touchedTile.isInRowOrColumnOf(emptyTile)) {
return false;
}
else {
mediaPlayer.start();
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
movedTile = touchedTile;
currentMovement = getTilesBetweenEmptyTileAndTile(movedTile);
//movedTile.numberOfMoves = 0;
}
else if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
if (lastDragPoint != null) {
followFinger(event);
}
lastDragPoint = new PointF(event.getRawX(), event.getRawY());
}
else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
currentMovement = getTilesBetweenEmptyTileAndTile(movedTile);
if (lastDragMovedAtLeastHalfway() || isClicked()) {
animateTilesToEmptySpace();
checkMatch();
}
else {
animateTilesBackToOrigin();
}
currentMovement = null;
lastDragPoint = null;
movedTile = null;
}
return true;
}
}
Why don't you try to extend the view where you're setting the OnTouchListener and override the method onTouchEvent from that view. Call the super, after invoke your own methods, that way you ensure that they will be triggered before the onTouchLister. Let me know if that did the trick.
If you're looking for a complex listener, you should look at MotionEvent, So for MotionEvent.ACTION_UP the doc says:
... A pressed gesture has finished, the motion contains the final release location as well as any intermediate points since the last down or move event....
There is a lot of info about it.
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// I just put my finger on the view
return true;
case MotionEvent.ACTION_MOVE:
// I am moving my finger over the screen
return true;
case MotionEvent.ACTION_UP:
// I have just released my finger from the screen
return true;
default:
return super.onTouchEvent(event);
}
}
EDIT: looks like you're trying to make a jigsaw puzzle app with one empty cell.
Considering you must be using an animator object, u also must have registered an on animate update listener. Shouldn't you put the checkMatch() method inside it so that it gets called whenever the animation ends, since a match will occur only after the user moves tiles?
I have the following code, all I want is to detect touch on a edit text and scroll the scroll view:
View.OnTouchListener buttonFocus = new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("TOuch");
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("TOuch down");
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
break;
case MotionEvent.ACTION_UP:
System.out.println("TOuch up");
((ScrollView) findViewById(R.id.m_scroll)).fullScroll(View.FOCUS_DOWN);
v.performClick();
break;
default:
break;
}
return false;
}
};
While as the keypad pops up at the start of activity the scroll does not occur, it only scroll when I touch the view for the second time. Am I doing something wrong here? Will implementing a thread help?
Returning true in that last line of yours might solve your problem.
Returning false means that the onTouch has been used completely and won't entertain any further touch events
brown = (Button) findViewById(R.id.brownButton);
brown.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
count++;
Log.d("count", "" + count);
return true;
} else if (event.getAction() == (MotionEvent.ACTION_UP)) {
count--;
Log.d("count", "" + count);
return true;
}
return false;
}
});
When my finger presses and holds the button my count will only increment ONCE. When I let go it will decrement accordingly. Please can someone show me how I can get my code to increment as long as my finger is holding the button down. Thanks.
A touch listener works like this:
It receives a ACTION_DOWN event (the user touches)
It then receives all the following events (if true is returned) and treats all the following events as ACTION_MOVE events
The touch lisetener keeps receiving ACTION_MOVE events until it receives an ACTION_UP or ACTION_CANCEL event.
So ACTION_DOWN is only triggered once, then any number of ACTION_MOVE events, then either a single ACTION_UP or ACTION_CANCEL.
If you want your counter to increment you need to check for ACTION_MOVE events.
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
//do something on DOWN
break;
case MotionEvent.ACTION_MOVE:
//do something on MOVE
break;
case MotionEvent.ACTION_UP:
//do something on UP
break;
}
return true;
}
I also needed to do this, but this question was never adequately answered, even though it is fairly old. However, a good solution can be found here: android repeat action on pressing and holding a button
I am adapting that solution to this problem. I tested it and it works for me.
Button brown = (Button) findViewById(R.id.brownButton);
brown.setOnTouchListener(new View.OnTouchListener() {
private Handler mHandler;
private int count = 0;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mHandler != null)
return true;
mHandler = new Handler();
mHandler.postDelayed(mAction, 500);
break;
case MotionEvent.ACTION_UP:
if (mHandler == null)
return true;
mHandler.removeCallbacks(mAction);
mHandler = null;
break;
}
return true; // before I had written false
}
Runnable mAction = new Runnable() {
#Override
public void run() {
Log.d("count: ", "" + count);
count++;
mHandler.postDelayed(this, 500);
}
};
});
In the future I will apply this solution to a custom soft keyboard to allow the space and delete keys to continue working when being held down.
Update: Today I finally got around to making the continuous delete key. I thought I had tested my above code before, but today when I was doing it I had to return true from the onTouch method in order to get it to work.
Alright so i have 4 image views..
How can i control what happens when they are pressed down and up
So say like:
one of the images is press down a textview gets changed to 1 but when they let go of that one the text will change back to 0?
You need to use an OnTouchListener and have it listen for TouchEvents on your Views.
For Example:
MyTouchListener l = new MyTouchListener();
view.setOnTouchListener(l);
Here is an example of what MyTouchListener could look like:
class MyOnTouchListener implements OnTouchListener {
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
// touch down code
break;
case MotionEvent.ACTION_MOVE:
// touch move code
break;
case MotionEvent.ACTION_UP:
// touch up code
break;
}
return true;
}
}