Android - Receiving wrong touch events - android

Here is the code I am using:
// Handling Touch Events
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
float onOffBitmapWidth = this.onOffBitmap.getWidth();
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
if (this.touchMove) {
if (togglePositionX > this.onOffBitmap.getWidth() / 4.0f) {
togglePositionX = this.onOffBitmap.getWidth() / 2.0f - this.toggleBitmap.getWidth()/4.0f;
} else if (togglePositionX <= this.onOffBitmap.getWidth() / 4.0f) {
togglePositionX = 0.0f;
}
this.invalidate();
this.touchMove = false;
return true;
} else {
return false;
}
} else if (motionEvent.getAction() == MotionEvent.ACTION_CANCEL) {
this.touchMove = false;
this.invalidate();
} else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
this.touchMove = true;
float currentX = motionEvent.getX();
if (currentX > 0.0f && currentX < (this.onOffBitmap.getWidth() / 2.0f - this.toggleBitmap.getWidth()/4.0f)) {
togglePositionX = currentX;
} else if (currentX >= (this.onOffBitmap.getWidth() / 2.0f - this.toggleBitmap.getWidth()/4.0f)) {
togglePositionX = this.onOffBitmap.getWidth() / 2.0f - this.toggleBitmap.getWidth()/4.0f;
} else if (currentX <= 0.0f) {
togglePositionX = 0.0f;
}
this.invalidate();
return true;
}
return true;
}
#Override
public void onClick(View v) {
if (togglePositionX == 0.0f) {
togglePositionX = this.onOffBitmap.getWidth() / 2.0f - this.toggleBitmap.getWidth()/4.0f;
} else {
togglePositionX = 0.0f;
}
this.invalidate();
}
I am using onClick event for single tap event. The problem is that ACTION_MOVE is called even if I only tap the screen. I even do it in a funny way (with the very tip of my finger).

I end up using an array list containing the history of touch positions that the user performs on the view + a flag to detect wether it's a real ACTION_MOVE or not. Here is the code that I implemented inside if (motionEvent.getAction() == MotionEvent.ACTION_MOVE)
float currentX = motionEvent.getX();
this.userTouchMoveArray.add(currentX);
if (this.userTouchMoveArray.size() == 1) {
touchIsMoved = false;
} else {
float oldestX = userTouchMoveArray.get(0);
if (Math.abs(currentX - oldestX) > 2.0f) {
touchIsMoved = true;
} else {
touchIsMoved = false;
}
}
Working like a charm. (You can define your own tolerance, here I am using 2 px)

Related

How can I return the image inside the screen if was outside the screen using onTouch?

I have an image and that image can be moved by touching. Everything is working, but I want to make this image cannot be outside of the screen. Here is my trying:
Code
#Override
public boolean onTouch(View v, MotionEvent event) {
if (v.getId() == R.id.layout_foreground_image_view_533) {
if (event.getAction() == MotionEvent.ACTION_DOWN)
return true;
else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (v.getX() < 0)
v.setX(0);
else if (v.getX() + v.getWidth() > displayMetrics.widthPixels)
v.setX(displayMetrics.widthPixels - v.getWidth());
else
v.setX(event.getRawX() - (float) v.getWidth() / 2);
return true;
}
}
return false;
}
The code will check if the image was less than 0 and will return it to 0. But I want the image cannot to be outside the screen ever, because if you try the code you will notice there is vibration.
Solved
#Override
public boolean onTouch(View v, MotionEvent event) {
if (v.getId() == R.id.layout_foreground_image_view_533) {
if (event.getAction() == MotionEvent.ACTION_DOWN)
return true;
else if (event.getAction() == MotionEvent.ACTION_MOVE) {
//For Width
if (event.getRawX() - (float) v.getWidth() / 2 < 0)
v.setX(0);
else if (event.getRawX() + (float) v.getWidth() / 2 > displayMetrics.widthPixels)
v.setX(displayMetrics.widthPixels - v.getWidth());
else
v.setX(event.getRawX() - (float) v.getWidth() / 2);
//For Height
if (event.getRawY() - (float) v.getHeight() / 2 < 0)
v.setY(0);
else if (event.getRawY() + (float) v.getHeight() / 2 > displayMetrics.heightPixels)
v.setY(displayMetrics.heightPixels - v.getHeight());
else
v.setY(event.getRawY() - (float) v.getHeight() / 2);
return true;
}
}
return false;
}

Detect touch outside of View

I have a custom View and I wonder, is there any chace that I could detect if something outside of my View is clicked. It MUST be in the View class, or else it can not work as I intended it to!
Full file: https://github.com/Nicba1010/AndroidLibrary/blob/master/src/com/nicba1010/utils/views/PieChartView.java
OnTouchEvent(so you know I did something)
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
selected = null;
invalidate();
return true;
}
double deltaX = event.getX() - rect.width() / 2 - rect.left;
double deltaY = -(event.getY() - rect.height() / 2 - rect.top);
double fromMid = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
boolean inCircle = fromMid < (rectSelect.bottom / 2);
if (inCircle) {
double angleInDegrees = getPositionOnCircumference(deltaX, deltaY);
float percentage = (float) (angleInDegrees / 360f);
float totalPerc = 0;
int index = -1, i = 0;
for (PieChartSlice e : slices) {
if (percentage > totalPerc) {
index = i;
} else if (percentage < totalPerc) {
break;
}
totalPerc += e.getPercentage();
i++;
}
if (index == -1) {
Toast.makeText(getContext(), "ERROR", Toast.LENGTH_LONG).show();
} else {
final PieChartSlice tmp = slices.get(index);
addScaleTask(rect, 500, 0.9f, new Runnable() {
#Override
public void run() {
selected = tmp;
}
});
selected = slices.get(index);
invalidate();
Toast.makeText(getContext(), selected.getName(),
Toast.LENGTH_SHORT).show();
onSliceSelectedListener.onSliceSelected(this, selected);
}
} else {
if (selected != null) {
addScaleTask(rect, 500, 1f, new Runnable() {
#Override
public void run() {
selected = null;
}
});
invalidate();
}
}
return super.onTouchEvent(event);
}
View has the getHitRect(Rect) method. You can use to retrieve it hit rect of this view and you can use this rect to check it the MotionEvent it is inside it. For instance
Rect rect = new Rect();
getHitRect(rect);
if (rect.contains((int) event.getX(), (int) event.getY()) {
}

Why Swipe left doesn't work?

I wrote the below code to detect and perform a sprite action on the single tap and swipe right event.
#Override
public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
float x = 0F;
int tapCount = 0;
boolean playermoving = false;
// TODO Auto-generated method stub
if (pSceneTouchEvent.getAction() == MotionEvent.ACTION_MOVE) {
if (pSceneTouchEvent.getX() > x) {
playermoving = true;
players.runRight();
}
if (pSceneTouchEvent.getX() < x) {
Log.i("Run Left", "SPRITE Left");
}
/*
* if (pSceneTouchEvent.getX() < x) { System.exit(0);
* Log.i("SWIPE left", "SPRITE LEFT"); }
*/
}
if (pSceneTouchEvent.getAction() == MotionEvent.ACTION_DOWN) {
playermoving = false;
x = pSceneTouchEvent.getX();
tapCount++;
Log.i("X CORD", String.valueOf(x));
}
if (pSceneTouchEvent.isActionDown()) {
if (tapCount == 1 && playermoving != true) {
tapCount = 0;
players.jumpRight();
}
}
return true;
}
The code works fine. The only problem is that the swipe left event is not being detected due to some reasons. What can i do to make the swipe left action work? Please help
because your x is always 0 and pSceneTouchEvent.getX() cant be negativ, so it will always just do "run right". this is because you set your x=0 at each new event.

Rotation around 3d object on touch using min3d/Rajawali framework (android)

I am working on an algorithm for rotating the camera around a 3D object using the Min3d/Rajawali framework.
With my implementation, the rotation around axis X is not working properly. I think the method setLookAt() is not working properly.
The problem:
When I rotate the sphere vertically, I can't fully see it. For example, turning the planet Earth, I can not fully see the Antarctic, because the algorithm resets the camera down.
Is it possible to realize the camera rotation around an object without using the method "setLookAt"?
I have tried different solutions, but have not been able to get it working correctly.
Below is my code:
initScene:
scene.camera().position.z = 90;
scene.camera().target = raketeOBJ.position();
onTouchEvent:
public boolean onTouchEvent(MotionEvent me) {
if (me.getAction() == MotionEvent.ACTION_DOWN) {
xpos = me.getX();
ypos = me.getY();
return true;
}
if (me.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
touchTurn = 0;
touchTurnUp = 0;
return true;
}
if (me.getAction() == MotionEvent.ACTION_MOVE) {
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;
xpos = me.getX();
ypos = me.getY();
touchTurn = xd / -200f;
touchTurnUp = yd / -200f;
return true;
}
try {
Thread.sleep(15);
} catch (Exception e) {
}
return super.onTouchEvent(me);
}
UpdateScene:
if (touchTurn != 0) {
scene.camera().position.rotateY(touchTurn);
touchTurn = 0;
}
if (touchTurnUp != 0) {
scene.camera().position.rotateX(touchTurnUp);
touchTurnUp = 0;
}
Number3d target = scene.camera.target;
Number3d cp = scene.camera.position.clone();
// move position like target is (0,0,0)
cp.x -= target.x;
cp.y -= target.y;
cp.z -= target.z;
cp.roateX(angle);
// restore offset
cp.x += target.x;
cp.y += target.y;
cp.z += target.z;
scene.camera.position.setAllFrom(cp);
A bit late but in case anyone has trouble with that, like me.
Rajawali offers a camera called ArcballCamera, which does exactly what you/we are trying to do.
In your Renderer add the following:
ArcballCamera arcball = new ArcballCamera(mContext, ((Activity)mContext).findViewById(R.id.contentView));
arcball.setTarget(mObjectGroup); //your 3D Object
arcball.setPosition(0,0,4); //optional
getCurrentScene().replaceAndSwitchCamera(getCurrentCamera(), arcball);
now you can rotate and zoom the object without a dozen lines of code.
setLookAt() needs to be called onDrawFrame if you want the camera to update regularly. But you need to care more about creating a "RotateAroundAnimation". See here for more info: http://www.rozengain.com/blog/2012/03/26/rajawali-tutorial-12-animation-classes/
You'll have to make yourAnimation.setTransformable3D(mCamera), and then it should control your main camera. I often use this methodology and then call the "yourAnimation.start()" on touch, or other external stimulus.
This is my way to rotate 3d model according as x and y
in Render class
public boolean left, right;
public boolean up, down;
and in onDrawFrame
#Override
public void onDrawFrame(GL10 glUnused) {
super.onDrawFrame(glUnused);
// getCurrentCamera().setRotY(getCurrentCamera().getRotY() + 1);
if (left) {
getCurrentCamera().setRotX(getCurrentCamera().getRotX() - 1);
}
if (right) {
getCurrentCamera().setRotX(getCurrentCamera().getRotX() + 1);
}
if (up) {
getCurrentCamera().setRotY(getCurrentCamera().getRotY() - 1);
}
if (down) {
getCurrentCamera().setRotY(getCurrentCamera().getRotY() + 1);
}
}
and in MainActivity
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
xpos = event.getX();
ypos = event.getY();
}
if (event.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
mRender.left = false;
mRender.right = false;
mRender.up = false;
mRender.down = false;
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
xd = event.getX() - xpos;
yd = event.getY() - ypos;
xpos = event.getX();
ypos = event.getY();
if (xd < 0) {
mRender.up = true;
} else {
mRender.down = true;
}
if (yd < 0) {
mRender.left = true;
} else {
mRender.right = true;
}
}
return super.onTouchEvent(event);
}
by this way, i can rotate my model :)

Stop OnLongClickListener from firing while dragging

I have a custom View with bitmaps on it that the user can drag about.
I want to make it so when they long click one of them I can pop up a context menu with options such as reset position etc.
In the custom View I add my OnLongClickListener:
this.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
// show context menu..
return true;
}
});
And override onTouchEvent to look something like this:
public boolean onTouchEvent(MotionEvent event) {
handleDrag(event);
super.onTouchEvent(event);
return true;
}
The handleDrag function finds what object is been pressed, and handles updating it's position.
My problem is that when I start to drag an image the OnLongClickListener fires also. I'm not sure the best way around this.
I've tried adding a threshold to handleDrag to return false if user touches down but doesn't attempt to drag, but I'm finding it still difficult to get the correct handler fired.
Can anyone suggest a way to skip the OnLongClickListener while dragging?
I think I have this solved through tweaking my threshold approach.
First, I changed my onTouchEvent to look like this:
public boolean onTouchEvent(MotionEvent event) {
mMultiTouchController.handleDrag(event);
return super.onTouchEvent(event);
}
They now both fire, so I then changed my OnLongClickListener to the following:
this.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
if (!mMultiTouchController.has_moved) {
// Pop menu and done...
return false;
}
return true;
}
});
(mMultiTouchController is the class containing all my gesture detection code).
The key here is within this class, I added the bool 'has_moved'. When I go to start a drag I then compute the delta:
float diffX = Math.abs(mCurrPtX - mPrevPt.getX());
float diffY = Math.abs(mCurrPtY - mPrevPt.getY());
if (diffX < threshold && diffY < threshold) {
has_moved = false;
return;
}
Now when the onLongClick fires I know whether to take action or not.
The final piece was to set:
setHapticFeedbackEnabled(false);
in my View so that the user doesn't get a vibrate every time the longClick fires but no action is taken. I plan to do the vibration manually as a next step.
This seems to be ok so far, hope that helps anyone who has come across a similar situation as this one.
I would stop using the onLongClickListener and just implement your own, which is pretty easy to do. Then you have the control you need to keep them from interfering with each other.
The following code implements the following gestures: drag, tap, double tap, long click, and pinch.
static final short NONE = 0;
static final short DRAG = 1;
static final short ZOOM = 2;
static final short TAP = 3;
static final short DOUBLE_TAP = 4;
static final short POST_GESTURE = 5;
short mode = NONE;
static final float MIN_PINCH_DISTANCE = 30f;
static final float MIN_DRAG_DISTANCE = 5f;
static final float DOUBLE_TAP_MAX_DISTANCE = 30f;
static final long MAX_DOUBLE_TAP_MS = 1000;
static final long LONG_PRESS_THRESHOLD_MS = 2000;
public class Vector2d {
public float x;
public float y;
public Vector2d() {
x = 0f;
y = 0f;
}
public void set(float newX, float newY) {
x = newX;
y = newY;
}
public Vector2d avgVector(Vector2d remote) {
Vector2d mid = new Vector2d();
mid.set((remote.x + x)/2, (remote.y + y)/2);
return mid;
}
public float length() {
return (float) Math.sqrt(x * x + y * y);
}
public float distance(Vector2d remote) {
float deltaX = remote.x - x;
float deltaY = remote.y - y;
return (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
}
}
private Vector2d finger1 = new Vector2d();
private Vector2d finger2 = new Vector2d();
private Vector2d pinchStartDistance = new Vector2d();
private Vector2d pinchMidPoint;
private Vector2d fingerStartPoint = new Vector2d();
private long gestureStartTime;
private Marker selectedMarker;
#Override
public boolean onTouch(View v, MotionEvent event) {
// Dump touch event to log
dumpEvent(event);
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
finger1.set(event.getX(), event.getY());
if (mode == TAP) {
if (finger1.distance(fingerStartPoint) < DOUBLE_TAP_MAX_DISTANCE) {
mode = DOUBLE_TAP;
} else {
mode = NONE;
gestureStartTime = SystemClock.uptimeMillis();
}
} else {
gestureStartTime = SystemClock.uptimeMillis();
}
fingerStartPoint.set(event.getX(), event.getY());
break;
case MotionEvent.ACTION_POINTER_DOWN:
finger2.set(event.getX(1), event.getY(1));
pinchStartDistance.set(Math.abs(finger1.x - finger2.x), Math.abs(finger1.y - finger2.y));
Log.d(TAG, String.format("pinch start distance = %f, %f", pinchStartDistance.x, pinchStartDistance.y));
if (pinchStartDistance.length() > MIN_PINCH_DISTANCE) {
if (pinchStartDistance.x < MIN_PINCH_DISTANCE) {
pinchStartDistance.x = MIN_PINCH_DISTANCE;
}
if (pinchStartDistance.y < MIN_PINCH_DISTANCE) {
pinchStartDistance.y = MIN_PINCH_DISTANCE;
}
pinchMidPoint = finger1.avgVector(finger2);
mode = ZOOM;
Log.d(TAG, "mode=ZOOM" );
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
if (mode == ZOOM) {
Vector2d pinchEndDistance = new Vector2d();
pinchEndDistance.set(Math.abs(finger1.x - finger2.x), Math.abs(finger1.y - finger2.y));
if (pinchEndDistance.x < MIN_PINCH_DISTANCE) {
pinchEndDistance.x = MIN_PINCH_DISTANCE;
}
if (pinchEndDistance.y < MIN_PINCH_DISTANCE) {
pinchEndDistance.y = MIN_PINCH_DISTANCE;
}
Log.d(TAG, String.format("pinch end distance = %f, %f", pinchEndDistance.x, pinchEndDistance.y));
zoom(pinchMidPoint, pinchStartDistance.x/pinchEndDistance.x, pinchStartDistance.y/pinchEndDistance.y);
// Set mode to "POST_GESTURE" so that when the other finger lifts the handler won't think it was a
// tap or something.
mode = POST_GESTURE;
} else if (mode == NONE) {
// The finger wasn't moved enough for it to be considered a "drag", so it is either a tap
// or a "long press", depending on how long it was down.
if ((SystemClock.uptimeMillis() - gestureStartTime) < LONG_PRESS_THRESHOLD_MS) {
Log.d(TAG, "mode=TAP");
mode = TAP;
selectedMarker = checkForMarker(finger1);
if (selectedMarker != null) {
Log.d(TAG, "Selected marker, mode=NONE");
mode = NONE;
((Activity) parent).showDialog(ResultsActivity.DIALOG_MARKER_ID);
}
}
else {
Log.d(TAG, "mode=LONG_PRESS");
addMarker(finger1);
requestRender();
}
} else if (mode == DOUBLE_TAP && (SystemClock.uptimeMillis() - gestureStartTime) < MAX_DOUBLE_TAP_MS) {
// The finger was again not moved enough for it to be considered a "drag", so it is
// a double-tap. Change the center point and zoom in.
Log.d(TAG, "mode=DOUBLE_TAP");
zoom(fingerStartPoint, 0.5f, 0.5f);
mode = NONE;
} else {
mode = NONE;
Log.d(TAG, "mode=NONE" );
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == NONE || mode == TAP || mode == DOUBLE_TAP) {
finger1.set(event.getX(), event.getY());
if (finger1.distance(fingerStartPoint) > MIN_DRAG_DISTANCE) {
Log.d(TAG, "mode=DRAG" );
mode = DRAG;
scroll(fingerStartPoint.x - finger1.x, fingerStartPoint.y - finger1.y);
}
}
else if (mode == DRAG) {
scroll(finger1.x - event.getX(), finger1.y - event.getY());
finger1.set(event.getX(), event.getY());
}
else if (mode == ZOOM) {
for (int i=0; i<event.getPointerCount(); i++) {
if (event.getPointerId(i) == 0) {
finger1.set(event.getX(i), event.getY(i));
}
else if (event.getPointerId(i) == 1) {
finger2.set(event.getX(i), event.getY(i));
}
else {
Log.w(TAG, String.format("Unknown motion event pointer id: %d", event.getPointerId(i)));
}
}
}
break;
}
return true;
}
/** Show an event in the LogCat view, for debugging */
private void dumpEvent(MotionEvent event) {
String names[] = { "DOWN" , "UP" , "MOVE" , "CANCEL" , "OUTSIDE" ,
"POINTER_DOWN" , "POINTER_UP" , "7?" , "8?" , "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_" ).append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN
|| actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid " ).append(
action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")" );
}
sb.append("[" );
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#" ).append(i);
sb.append("(pid " ).append(event.getPointerId(i));
sb.append(")=" ).append((int) event.getX(i));
sb.append("," ).append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";" );
}
sb.append("]" );
Log.d(TAG, sb.toString());
}
//This code is to handle the gestures detection
final Handler handler = new Handler();
private Runnable mLongPressRunnable;
detector = new GestureDetector(this, new MyGestureDectector());
view.setOnTouchListener(new OnTouchListener() {
#SuppressLint("ClickableViewAccessibility")
#SuppressWarnings("deprecation")
#Override
public boolean onTouch(View v, MotionEvent event) {
detector.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
handler.postDelayed(mLongPressRunnable, 1000);
}
if ((event.getAction() == MotionEvent.ACTION_MOVE)
|| (event.getAction() == MotionEvent.ACTION_UP)) {
handler.removeCallbacks(mLongPressRunnable);
}
}
return true;
}
});
mLongPressRunnable = new Runnable() {
public void run() {
Toast.makeText(MainActivity.this, "long", Toast.LENGTH_SHORT)
.show();
}
};
class MyGestureDectector implements GestureDetector.OnDoubleTapListener,
OnGestureListener {
//Implement all the methods
}

Categories

Resources