MY imageview does not move when ontouch event is called...Though I can see ACTION_UP and ACTION_MOVE happening from the log traces.
Here is my code:-
public boolean onTouch(View v, MotionEvent motion) {
float dy ;
float dx;
double r = 0;
float angle = 0;
switch(v.getId())
{
case R.id.arrow:
switch(motion.getActionMasked())
{
case MotionEvent.ACTION_MOVE:
Log.w(this.getClass().getName(),"In MOTION MOVE");
mX1 = motion.getX();
mY1 = motion.getY();
break;
case MotionEvent.ACTION_UP:
Log.w(this.getClass().getName(),"In MOTION UP");
mX2 = motion.getX();
mY2 = motion.getY();
dy = -( mY2-mY1);
dx = -(mX2-mX1);
r = Math.atan2(dy, dx);
angle = (int)Math.toDegrees(r);
updateRotation(angle);
break;
default:
break;
}
Log.d(this.getClass().getName(), "Value of angle in ontouch: " + Double.toString(angle));
break;
default:
break;
}
return true;
}
Basically I am trying to calculate the angular displacement when user touches the imageview and changes its position.
======================================
Edit:- Below is my update rotation function:-
public void updateRotation(double angle)
{
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.arrow);
int w = bitmap.getWidth();
int h = bitmap.getHeight();
String filePath = null;
Matrix mtx = new Matrix();
Log.d(this.getClass().getName(), "Value: " + Double.toString(angle));
if(angle > 90)
angle = 90;
mtx.postRotate((float) angle);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
Drawable draw =new BitmapDrawable(bitmap);
mImageView.setBackgroundDrawable(draw);
}
=========================================================
Edit 2: Modified function
public boolean onTouch(View v, MotionEvent motion) {
float dy ;
float dx;
double r = 0;
switch(v.getId())
{
case R.id.arrow:
switch(motion.getActionMasked())
{
case MotionEvent.ACTION_DOWN:
mX1 = motion.getX();
mY1 = motion.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.w(this.getClass().getName(),"In MOTION MOVE");
mX1 = motion.getX();
mY1 = motion.getY();
break;
case MotionEvent.ACTION_UP:
Log.w(this.getClass().getName(),"In MOTION UP");
mX2 = motion.getX();
mY2 = motion.getY();
dy = -( mY2-mY1);
dx = -(mX2-mX1);
r = Math.atan2(dy, dx);
mAngle = (int)Math.toDegrees(r);
updateRotation();
break;
default:
break;
}
Log.d(this.getClass().getName(), "Value of angle in ontouch: " + Float.toString(mAngle));
break;
default:
break;
}
return true;
}
=============================================
Edit 3:- Strange thing is that if i modify my onTouch event and perform all the calculations in ACTION_MOVE the imageview responds for each touch event, but this is not what I want as I cannot do the proper angluar rotation in ACTION_MOVE event
Ruchira,
From looking at your code and working with many onTouch innovations myself, I can tell you that the problem is definitely in your onTouchEvent(). You record the mX1 and mY1 in your ACTION_MOVE without recording the original ACTION_DOWN event. This means that every time you move your finger, the onTouchEvent() method thinks that this is the original position. Try this instead:
public boolean onTouch(View v, MotionEvent motion) {
float dy ;
float dx;
double r = 0;
float angle = 0;
switch(v.getId())
{
case R.id.arrow:
switch(motion.getActionMasked())
{
case MotionEvent.ACTION_DOWN:
mX1 = motion.getX();
mY1 = motion.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.w(this.getClass().getName(),"In MOTION MOVE");
//Just to support real time rotation, you could:
mX2 = motion.getX();
mY2 = motion.getY();
//... some rotation code.
break;
case MotionEvent.ACTION_UP:
Log.w(this.getClass().getName(),"In MOTION UP");
// All of this should be fine.
mX2 = motion.getX();
mY2 = motion.getY();
dy = -( mY2-mY1);
dx = -(mX2-mX1);
r = Math.atan2(dy, dx);
angle = (int)Math.toDegrees(r);
updateRotation(angle);
break;
default:
break;
}
Log.d(this.getClass().getName(), "Value of angle in ontouch: " + Double.toString(angle));
break;
default:
break;
}
return true;
}
That should at least track your numbers correctly, so that they may actually be used.
Additional Considerations
The most common mistake is to make sure that you have not set any of your tracking variables to final. If this is the case, the code will set it once, but not again...
When dealing with Drawables, it is important to get rid of the original Drawable and its callback. The reason for this is a well known issue that was never resolved in Android because a decision couldn't be made as to when exactly it was necessary and when it wasn't. Essentially, Android will eventually run out of memory, but will not necessarily tell you and just fail to update the image. This is how you do this (when replacing an image).
Drawable trash = mImageView.getBackgroundDrawable();
if (trash != null)
{ trash.setCallback(null);
trash.recycle();
trash = null;
}
mImageView.setBackgroundDrawable(draw);
Finally, you have to let the image know that it has to redraw itself. This isn't always the case, but is true more often than not. mImageView.invalidate(); will often resolve the issue, and should be placed on the last line in your updateRotation() method.
FuzzicalLogic
Related
I need to move, rotate and resize edittext based on user's touch gestures.
Here is what I am doing.
1. Listen for ACTION_DOWN event to detect single finger touch and then use that for movement.
Listen for ACTION_POINTER_DOWN, and get the index count to detect two finger touch and then use ACTION_MOVE for rotation.
I need to make resize work such that only the width gets decreased on resize by touch.
Problem I am facing
Movement is working perfectly fine. But when I apply rotation using the ACTION_POINTER_DOWN, rotation goes all sorts of haywire. Any suggestions on how to make the view movement and rotation work together?
Here is the code for touchListener I have applied
tvMain.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
moving = true;
break;
case MotionEvent.ACTION_POINTER_DOWN:
Log.d("ActionPointerIndex", "" + event.getActionIndex());
int index = event.getActionIndex();
moving = false;
//FOR ROTATION DETAILS
xc = tvMain.getWidth() / 2;
yc = tvMain.getHeight() / 2;
x_ = event.getX();
y_ = event.getY();
Log.d("currentAnglePD", ": " + x_ + "," + y_);
if (index == 1) {
rotate = true;
mCurrAngle = Math.toDegrees(Math.atan2(x_ - xc, yc - y_));
moving = false;
}
break;
case MotionEvent.ACTION_MOVE:
if (moving) {
leftBound = rootView.getX();
topBound = rootView.getY();
rightBound = rootView.getX() + 1280;
bottomBound = rootView.getY() + 618;
x = event.getRawX() - tvMain.getWidth() / 2;
y = event.getRawY() - tvMain.getHeight();
dx = Float.valueOf(rootView.getWidth() - tvMain.getWidth());
dy = Float.valueOf(rootView.getHeight() - tvMain.getHeight());
bottomBound_ = tvMain.getY() + tvMain.getHeight();
rightBound_ = tvMain.getX() + tvMain.getWidth();
if (x >= leftBound && y >= topBound && x <= dx && y <= dy) {
tvMain.setX(x);
tvMain.setY(y);
}
}
if (rotate) {
Log.d("currentAngleMove", ": " + x_ + "," + y_);
mPrevAngle = mCurrAngle;
mCurrAngle = Math.toDegrees(Math.atan2(x_ - event.getX(), yc - event.getY()));
animate(mPrevAngle, mCurrAngle, 0);
System.out.println(mCurrAngle);
}
break;
case MotionEvent.ACTION_POINTER_UP:
rotate = false;
break;
case MotionEvent.ACTION_UP:
rotate = false;
moving = false;
//tvMain.clearAnimation();
break;
}
return true;
}
});
Movement is working fine, but once I move the view, rotation goes haywire, and it does not rotate along its center. Once I stop rotating, the view stops listening for any more touch gestures.
I'm working with OpenGLES 2.0 in android. I need to move an OpenGL object with the taking the x and y axis from the touch event. But when I try to translate the object it acts weird. Same object gets displayed in two places with varying opacity.. and when the finger is lifted both the objects get disappeared.
My translation code is:
Matrix.setIdentityM(mtrxView, 0);
Matrix.translateM(mtrxView, 0, x, y, 0);
Matrix.multiplyMM(mtrxProjectionAndView, 0, mtrxProjection, 0, mtrxView, 0);
I'm overriding onTouch() method to handle the ACTION_MOVE event:
public boolean onTouch(View v, MotionEvent ev) {
x = ev.getX();
y = ev.getY();
deltaX = x - downX;
deltaY = y - downY;
mScaleDetector.onTouchEvent(ev);
AppLog.v(TAG, "Screen xy cordinate is: " + x + " " + y + " ");
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if(!isZoomInProcess){
glRender.requestRender(x, y,GlSurfaceView.this);
requestRender();
}
break;
}
return true;
}
Im trying to do a pinch to zoom on a imageview. The problem is that the when i zoom it does'nt scale where i pinched it scales up to the left corner. Im not sure why i does this and i seems that there is a lot of people having the same problem but i havent found a soloution to it yet.
public override bool OnTouchEvent(MotionEvent ev)
{
_scaleDetector.OnTouchEvent(ev);
MotionEventActions action = ev.Action & MotionEventActions.Mask;
int pointerIndex;
switch (action)
{
case MotionEventActions.Down:
_lastTouchX = ev.GetX();
_lastTouchY = ev.GetY();
_activePointerId = ev.GetPointerId(0);
break;
case MotionEventActions.Move:
pointerIndex = ev.FindPointerIndex(_activePointerId);
float x = ev.GetX(pointerIndex);
float y = ev.GetY(pointerIndex);
if (!_scaleDetector.IsInProgress)
{
// Only move the ScaleGestureDetector isn't already processing a gesture.
float deltaX = x - _lastTouchX;
float deltaY = y - _lastTouchY;
_posX += deltaX;
_posY += deltaY;
Invalidate();
}
_lastTouchX = x;
_lastTouchY = y;
break;
case MotionEventActions.Up:
case MotionEventActions.Cancel:
// We no longer need to keep track of the active pointer.
_activePointerId = InvalidPointerId;
break;
case MotionEventActions.PointerUp:
// check to make sure that the pointer that went up is for the gesture we're tracking.
pointerIndex = (int) (ev.Action & MotionEventActions.PointerIndexMask) >> (int) MotionEventActions.PointerIndexShift;
int pointerId = ev.GetPointerId(pointerIndex);
if (pointerId == _activePointerId)
{
// This was our active pointer going up. Choose a new
// action pointer and adjust accordingly
int newPointerIndex = pointerIndex == 0 ? 1 : 0;
_lastTouchX = ev.GetX(newPointerIndex);
_lastTouchY = ev.GetY(newPointerIndex);
_activePointerId = ev.GetPointerId(newPointerIndex);
}
break;
}
return true;
}
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
canvas.Save();
canvas.Translate(_posX, _posY);
canvas.Scale(_scaleFactor, _scaleFactor, _lastTouchX, _lastTouchY);
_icon.Draw(canvas);
canvas.Restore();
}
I think it might have to do with this code in the beginning of the class where the bounds of the image is set to 0. But if i delete that code the image wont render.
public GestureRecognizer(Context context, ImageView imgview)
: base(context, null, 0)
{
//_icon = context.Resources.GetDrawable(Resource.Drawable.ic_launcher);
_icon = imgview.Drawable;
_icon.SetBounds(0, 0, _icon.IntrinsicWidth, _icon.IntrinsicHeight);
_scaleDetector = new ScaleGestureDetector(context, new MyScaleListener(this));
}
Have a look at MultiTouchController library. It makes it much easier to write multitouch applications for Android and pinch to zoom, rotate and drag is nicely implemented. Here is the link.
We have a longpress detection on a button. When detected, the view should be dismissed (back to the game view) and the object chosen with the button shall be at the coordinates of the longpress. That's the easy part, the hard part is to then change the coordinates with the finger, without releasing it.
Okay so i'm trying to reformulate this: We have two views displayed on top of eachother. What we want is a longclick detected in the top view, to then dismiss the top view, and without releasing the finger, get touchevents in the bottom view.
This is our class gameView: (this code contains a lot of code unneccesay for the problem)
#Override
public boolean onTouchEvent(MotionEvent event) {
gestureDetector.onTouchEvent(event);
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mode = DRAG;
//We assign the current X and Y coordinate of the finger to startX and startY minus the previously translated
//amount for each coordinates This works even when we are translating the first time because the initial
//values for these two variables is zero.
startX = event.getX() - previousTranslateX;
startY = event.getY() - previousTranslateY;
break;
case MotionEvent.ACTION_MOVE:
translateX = event.getX() - startX;
translateY = event.getY() - startY;
gestureHandler.setTranslation(translateX, translateY);
//We cannot use startX and startY directly because we have adjusted their values using the previous translation values.
//This is why we need to add those values to startX and startY so that we can get the actual coordinates of the finger.
double distance = Math.sqrt(Math.pow(event.getX() - (startX + previousTranslateX), 2) +
Math.pow(event.getY() - (startY + previousTranslateY), 2)
);
if(distance > 0) {
dragged = true;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = ZOOM;
scaleToX = event.getX();
scaleToY = event.getY();
break;
case MotionEvent.ACTION_UP:
mode = NONE;
dragged = false;
//All fingers went up, so let's save the value of translateX and translateY into previousTranslateX and
//previousTranslate
previousTranslateX = translateX;
previousTranslateY = translateY;
int x = (int)((-translateX/scaleFactorX)+(event.getX()/this.scaleFactorX));
int y = (int)((-translateY/scaleFactorY)+(event.getY()/this.scaleFactorY));
map.click(getContext(), x, y);
for(CastleTile castleTile : map.getCastleTiles()) {
if(castleTile.getRect().contains((int)((-translateX/scaleFactorX)+(event.getX()/this.scaleFactorX)), (int)((-translateY/scaleFactorY)+(event.getY()/this.scaleFactorY)))){
Log.d("Castle to your service", "Boes");
castleSettings.show();
}
}
break;
case MotionEvent.ACTION_POINTER_UP:
mode = DRAG;
//This is not strictly necessary; we save the value of translateX and translateY into previousTranslateX
//and previousTranslateY when the second finger goes up
previousTranslateX = translateX;
previousTranslateY = translateY;
break;
}
detector.onTouchEvent(event);
//We redraw the canvas only in the following cases:
//
// o The mode is ZOOM
// OR
// o The mode is DRAG and the scale factor is not equal to 1 (meaning we have zoomed) and dragged is
// set to true (meaning the finger has actually moved)
if ((mode == DRAG && scaleFactorX != 1f && scaleFactorY != 1f && dragged) || mode == ZOOM) {
invalidate();
}
return true;
}
#Override
public boolean onLongClick(View v) {
castleSettings.dismiss();
return true;
}
And this is the gestureHandler:
public void onLongPress(MotionEvent event) {
map.longClick(context, (int)((-translateX/scaleFactorX)+(event.getX()/this.scaleFactorX)), (int)((-translateY/scaleFactorY)+(event.getY()/this.scaleFactorY)));
}
Sorry for eventual lack of information or bad explaination :)
I am trying to rotate one of the transparent PNGs on this image. The number part is what I want to rotate. I am able to do this, but it is not what I am trying to achieve
I want to rotate the numbers like on a real combination lock. So the user will touch and move their finger in a circle. I looked at less precise image rotation on touch/move events, and they were not sufficient.
this is currently my code
public boolean onTouch(View v, MotionEvent event) {
double r=Math.atan2(event.getX()-lockNumbers.getWidth(), lockNumbers.getHeight()-event.getY());
int rotation=(int)Math.toDegrees(r);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
x=event.getX();
y=event.getY();
updateRotation(rotation);
break;
case MotionEvent.ACTION_UP:
break;
}//switch
return true;
}//onTouch
private void updateRotation(double rot){
float newRot=new Float(rot);
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.numbers);
Matrix matrix=new Matrix();
matrix.postRotate(newRot,bitmap.getWidth(),bitmap.getHeight());
if(y>250){
Bitmap reDrawnBitmap=Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true);
lockNumbers.setImageBitmap(reDrawnBitmap);
}
else{
Bitmap reDrawnBitmap=Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true);
lockNumbers.setImageBitmap(reDrawnBitmap);
}
}
it also resizes the bitmap when you touch it because of the matrix parameter. This is not the desired effect.
The user will pretty much be required to go in a circle with their finger.
Write below code into your touch event.
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// reset the touched quadrants
for (int i = 0; i < quadrantTouched.length; i++) {
quadrantTouched[i] = false;
}
allowRotating = false;
startAngle = getAngle(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
double currentAngle = getAngle(event.getX(), event.getY());
rotateDialer((float) (startAngle - currentAngle));
startAngle = currentAngle;
break;
case MotionEvent.ACTION_UP:
allowRotating = true;
break;
}
// set the touched quadrant to true
quadrantTouched[getQuadrant(event.getX() - (dialerWidth / 2), dialerHeight - event.getY() - (dialerHeight / 2))] = true;
detector.onTouchEvent(event);
return true;
And use below link for more reference.
Rotate Dialer Example
By accepting #Dipak Keshariya's answer i am putting way to to get the angle rotation which can help you identify selected part of wheel.
private float copy[] = new float[9];
matrix.getValues(copy);
float wheelAngle = Math.round(Math.atan2(copy[Matrix.MSKEW_X],
copy[Matrix.MSCALE_X]) * (180 / Math.PI));
I presume you want to rotate an image at the point where a user touches the screen? If so, extend the SimpleOnGestureListener like this example:
public class MyGestureDetector extends SimpleOnGestureListener
{
#Override
public void onLongPress(MotionEvent event)
{
int X = (int)event.getX();
int Y = (int)event.getY();
...Rotate the image
}
}
Once you've got the screen coordinates of the touch event, you could apply a Rotation Animation about the point - see here for more details: http://developer.android.com/guide/topics/graphics/2d-graphics.html