currently i am looking for ideas how to implement a dwelling time to the onTouch function. Normally the onTouch event does only process the events Up, Down, and Motion.
I want the user to be able to touch down anywhere, after that he can swipe to an object on the screen and if he stays still (=little to no movements) for a certain time an action is fired.
Is there a ways to get events for not moving while touching or any other type of event i can use for this behaviour? My current solution is rather... ugly
touch = new Vector2D(event.getX(),event.getY());
//if (not moving && touching && (System.nanoTime-currentNanoSeconds) > Value)
if(event.getAction()==MotionEvent.ACTION_MOVE){
currentNanoSeconds=System.nanoTime();
System.out.println(currentNanoSeconds);
}
if(event.getAction()== MotionEvent.ACTION_UP){
currentAngle=(float) angleBetween2Lines(midPoint, lineEnd, touch);
System.out.println((System.nanoTime()-currentNanoSeconds)/1000000);
touching=false;
checkTargets();
//forces redraw!
invalidate();
}
if(event.getAction()==MotionEvent.ACTION_DOWN)
touching=true;
return true;
thanks in advance.
I think you just need to add some spatial conditions to this answer. Something like (very loosely):
maxMovement = ?;
final Handler handler = new Handler();
Runnable mLongPressedinSameArea = new Runnable() {
public void run() {
if (*pseudo code*: the deltas between up/down X and Y are not greater than maxMovement){
Log.i("", "Long press in same area!");
}
}
};
#Override
public boolean onTouchEvent(MotionEvent event, MapView mapView){
if(event.getAction() == MotionEvent.ACTION_DOWN)
downX = event.getX();
downY = event.getY();
handler.postDelayed(mLongPressedinSameArea, 1000);//1 second linger time.
if(event.getAction() == MotionEvent.ACTION_UP))
upX = event.getX();
upY = event.getY();
handler.removeCallbacks(mLongPressedinSameArea);
return super.onTouchEvent(event, mapView);
}
Related
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.
I am using a small square RelativeLayout as a game pad for movement in a neighboring GLSurfaceView.
I set it up so that in the onTouch method of the RelativeLayout's onTouchListenner I call a method built into the GLSurfaceView that updates the translation and rotation coordinates for my drawing.
I have everything working fine, except for the fact that touch events are only triggered if the user moves his or her finger on the RelativeLayout.
I would like it to feel a little bit like a joystick: if you leave your finger pressed on the top of the RelativeLayout, then you will keep going "up" ( or more programatically: the event.getX() and event.getY() that were sent last should get looped, or re-sent, in the GLSurfaceView until the user either moves his finger inside the RelativeLayout, or stops touching the RelativeLayout altogether.)
What should I use to detect whether or not the RelativeLayout is currently being touched (even if there is no motion in the said touch)?
Thanks!
So this is what I came up pretty much simultaneously with csmcklvey
if(event.getAction()==MotionEvent.ACTION_UP)
gM.isTouchedL = false;
else
gM.isTouchedL = true;
return true;
Where .isTouchedL is the "control boolean" I use in the GLSurfaceView
Green lit csmc's answer anyways! :)
I have used rotation animation in one of my apps, which is using the onTouchEven of view. Major function has been performed in event "ACTION_MOVE". It will help you, to get through.
public boolean onTouch(View v, MotionEvent event)
{
final float xc = volumeButton.getWidth() / 2;
final float yc = volumeButton.getHeight() / 2;
final float x = event.getX();
final float y = event.getY();
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
{
// volumeButton.clearAnimation();
// mCurrAngle = Math.toDegrees(Math.atan2(x - xc, yc - y));
break;
}
case MotionEvent.ACTION_MOVE:
{
mPrevAngle = mCurrAngle;
mCurrAngle = Math.toDegrees(Math.atan2(x - xc, yc - y));
animate(mPrevAngle, mCurrAngle, 100);
break;
}
case MotionEvent.ACTION_UP :
{
mPrevAngle = mCurrAngle = 0;
break;
}
}
return true;
}
You might be able to use the event.getAction() method of the MotionEvent parameter of your onTouch() method. You can check to see if it is MotionEvent.ACTION_DOWN or MotionEvent.ACTION_UP.
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//start something
}
else if (event.getAction() == MotionEvent.ACTION_UP) {
//stop something
}
My thought is that you might be able to start or stop a thread in this manner but either way this might at least give you some more information about when the user actually presses and lifts his/her finger.
I want to have possibility to change position of items(textviews/imageview) on the screen by using touch. So I made OnTouchListener which looks like this:
float x = 0,y = 0 ;
private void clickOnObjectTaker() {
cream.setOnTouchListener(new OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
boolean touched= false;
if(event.getAction()==MotionEvent.ACTION_DOWN)
{
messageMaker("touched");
x= event.getX();
y= event.getY();
}
if(event.getAction()==MotionEvent.ACTION_MOVE )
{
Animation anim =
new TranslateAnimation(x, event.getX(), y, event.getY());
anim.setFillAfter(true);
anim.setFillEnabled(true);
cream.startAnimation(anim);
x= event.getX();
y= event.getY();
}
if(event.getAction()== MotionEvent.ACTION_CANCEL)
{
cream.setX(x);
cream.setY(y);
}
return true;
}});
}
My problem is that when i change the position of object, the listener don't get new position, so I see the object in different place on the screen, but when I want to move it I must click on old position. I thought i solve it by adding this lines:
if(event.getAction()== MotionEvent.ACTION_CANCEL)
{
cream.setX(x);
cream.setY(y);
}
but nothing changed.
UPDATE: I tried with this code. There |I haven't any problems with updating position element of layout, everything works properly except the process of moving object by touch.
What I don't like it in this method:
touched object vibrate when I move it (it doesn't look nice),
object doesn't follow perfectly my finger, like somewhere I should scale the coordinates ( for example I moved object from right to left side of screen, when my finger stops at the brink of screen, the object is lag behind of it.)
this two things makes this method useless for me.
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()== MotionEvent.ACTION_DOWN){
dx = event.getX()-v.getX();
dy = event.getY()-v.getY();
}
if(event.getAction()== MotionEvent.ACTION_MOVE)
{
v.setX(event.getX()-dx);
v.setY(event.getY()-dy);
}
return true;
}
}
The moving of oobject in method with animation looks great for me, everything works fluently. Only problem is connected with update layout, when I add code like this
if(event.getAction()==MotionEvent.ACTION_UP )
{
v.setX(event.getX());
v.setY(event.getY());
}
It put object at random position (I can't figure out, from what place he take this position), If somebody can give me any advice I would be grateful
EDITED
This code almost perfectly I could make repeatedly moves on view. It have two flaws:
small reallocation after Move gesture ends
During moving the view if I stop the finger on the screen, I can see the flashes of view at old position.
the code
OnTouchListener dealingwithproblems = new OnTouchListener()
{
#Override
public boolean onTouch( View v, MotionEvent event) {
if(event.getActionMasked()== MotionEvent.ACTION_DOWN){
//values for
dx = event.getX()-v.getX();
dy = event.getY()-v.getY();
x= event.getX();
y= event.getY();
}
if(event.getActionMasked()== MotionEvent.ACTION_MOVE)
{
Animation anim =
new TranslateAnimation(x, event.getX() , y, event.getY());
//anim.setFillAfter(true);
anim.setFillEnabled(true);
v.startAnimation(anim);
x= event.getX();
y= event.getY();
}
if(event.getActionMasked() == MotionEvent.ACTION_UP)
{
v.setX(event.getX()-dx);
v.setY(event.getY()-dy);
}
return true;
}
};
If somebody knew how to improve it, leave the answer pliz
it maybe animation problem, you are using ViewAnimation try to use change that in Object animator. it supports form API 11.
ObjectAnimator.ofFloat(view,"Translate",x,y,toX,toY);
I had a similar problem (touched object vibrates on move) and resolved it by using getRawX() and getRawY() instead of getX() and getY()
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;
}
This should be a pretty simple thing to do. The user puts his finger on the screen and drags it around the screen. There are two events firing on onTouch:
MotionEvent.ACTION_DOWN
MotionEvent.ACTION_MOVE
Now, how can I calculate the speed of the ACTION_MOVE gesture ? The user drags the finger slower or faster during a gesture, so I think I need to calculate the speed between two intermediate touched points: the lastTouchedPointX,lastTouchedPointY and the event.getX(),event.getY().
Has anyone done this before ?
What you need can be achieved by using the standard VelocityTracker class. More details on Google's Best Practice for User Input while tracking movement here. Most of the code below (which demonstrates the use of VelocityTracker by displaying the speed on X and Y axis of each fling/move) is taken from the previous resource link:
import android.os.Bundle;
import android.app.Activity;
import android.support.v4.view.VelocityTrackerCompat;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.widget.TextView;
public class MainActivity extends Activity {
private VelocityTracker mVelocityTracker = null;
private TextView mTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTextView = new TextView(this);
mTextView.setText("Move finger on screen to get velocity.");
setContentView(mTextView);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int index = event.getActionIndex();
int action = event.getActionMasked();
int pointerId = event.getPointerId(index);
switch (action) {
case MotionEvent.ACTION_DOWN:
if (mVelocityTracker == null) {
// Retrieve a new VelocityTracker object to watch the velocity
// of a motion.
mVelocityTracker = VelocityTracker.obtain();
} else {
// Reset the velocity tracker back to its initial state.
mVelocityTracker.clear();
}
// Add a user's movement to the tracker.
mVelocityTracker.addMovement(event);
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(event);
// When you want to determine the velocity, call
// computeCurrentVelocity(). Then call getXVelocity()
// and getYVelocity() to retrieve the velocity for each pointer ID.
mVelocityTracker.computeCurrentVelocity(1000);
// Log velocity of pixels per second
// Best practice to use VelocityTrackerCompat where possible.
mTextView.setText("X velocity: "
+ VelocityTrackerCompat.getXVelocity(mVelocityTracker,
pointerId)
+ "\nY velocity: "
+ VelocityTrackerCompat.getYVelocity(mVelocityTracker,
pointerId));
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
// Return a VelocityTracker object back to be re-used by others.
mVelocityTracker.recycle();
break;
}
return true;
}
}
#Override
public boolean onTouchEvent(MotionEvent event, MapView mapView) {
if(event.getAction() == MotionEvent.ACTION_DOWN) {
oldX = event.getX();
oldY = event.getY();
//start timer
} else if (event.getAction() == MotionEvent.ACTION_UP) {
//long timerTime = getTime between two event down to Up
newX = event.getX();
newY = event.getY();
float distance = Math.sqrt((newX-oldX) * (newX-oldX) + (newY-oldY) * (newY-oldY));
float speed = distance / timerTime;
}
}
It's a question about how accurate you want to perform this calculation.
However the basic procedure is to get the timestamp of each corresponding ACTION_DOWN, ACTION_UP couple and calculate the difference.
Then you need to determine the covered pixels. This could be done with simple trigonometry.
When you have both time difference and covered pixels you can calculate the pixels per time speed as an average of the two points (down and up).
You can do this for every point when the finger is moving over the screen to get a better result.
Image in Canvas with touch events
from this example get touch event time & calculate time & distance to get speed