Getting coordinates on touch event relative to scrollable map - android

Is there a way to get the coordinates of a touch event on a scrollable map?
Meaning when I touch at one point on the screen, getX() and getY() return the respective values and then when I scroll the map, the coordinates returned should be relative to the map, not the screen.
eg. I have a 750x750 background map, and my device screen size is 480x800.
when I first touch, say the coordinates returned are (100, 200). now when I scroll the map and touch somewhere else, I get the coordinates as 200, 200.
I want to get the coordinates with respect to the map and not the screen.
I've been trying to figure this out for a long time and have scoured the net and other sites in vain.
please help.
thanks in advance
\m/
i need the coordinates coz i'm developing a game in which i have a large map and objects placed on it. when i scroll the map, i need the objects to move along with in the same position.
here is my code:
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
touchStart.set(ev.getX(), ev.getY());
x = ev.getX();
y = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
newX = ev.getX() - touchStart.x + prevPicStart.x;
newY = ev.getY() - touchStart.y + prevPicStart.y;
if ((newX <= 0 && newX > 0 - mBG.getWidth() + Craz.DISP_WIDTH)) {
picStart.x = newX;
}
if ((newY <= 0 && newY > 0 - mBG.getHeight() + Craz.DISP_HEIGHT)) {
picStart.y = newY;
}
invalidate();
break;
case MotionEvent.ACTION_UP:
prevPicStart.x = picStart.x;
prevPicStart.y = picStart.y;
break;
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
canvas.drawBitmap(mBG, picStart.x, picStart.y, paint);
canvas.translate(picStart.x, picStart.y);
mBDoor.draw(canvas);
}

Its pretty easy. When you scroll your map, you have an offset. If you scroll to the right (the background will move to the left), your offset will be negative. Lets say, you have an offset on x of -50, and you click the screen coordinates 100 you simply do the math:
mapCoordX = screenX - offsetX; // makes: 100 - (-50) = 150
just looking at the X coordinate, for Y it should be the same.
I have written a tutorial about a map of tiles and scrolling over it. Maybe you take a look at it, too.
Pseudocode!
for (int id = 0; id < mapSize; id++) {
Tile tile = new Tile(id);
startX = id * tileWidth;
startY = id % rowLenght + id / rowLenght;
tile.setBackground(createCroppedBitmap(background, startX, startY));
}

Related

How to move an object in "X" and "Y" direction

I am working on android studio and I want to create the effect of throwing an object, in this case a circle drawn on the canvas. But I'm having problems. Can someone guide me?
I take the "X" and the "Y" value where the user touch but how can i make that the circle move in that direction?
Thanks
Game class:
public class Game extends SurfaceView implements View.OnTouchListener {
Paint paint;
int x, y, radius = 100, speedX = 5, speedY = 5, touchY, touchX;
boolean move = false;
boolean one_time = true;
public Game(Context context, AttributeSet attrs) {
super(context, attrs);
this.setOnTouchListener(this);
setFocusable(true);
paint = new Paint();
}
public void onDraw(Canvas canvas){
paint.setColor(Color.WHITE);
canvas.drawRect(0,0,canvas.getWidth(),canvas.getHeight(),paint);
if (one_time == true){
x = canvas.getWidth()/2;
y = canvas.getHeight()/2;
one_time = false;
}
paint.setColor(Color.BLACK);
canvas.drawCircle(x, y, radius, paint);
if (move == true){
if (x >= canvas.getWidth() - radius) {
speedX = -5;
}
if (x <= radius) {
speedX = 5;
}
if (y >= canvas.getHeight() - radius) {
speedY = -5;
}
if (y <= radius) {
speedY = 5;
}
x = x + speedX;
y = y + speedY;
}
invalidate();
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case (MotionEvent.ACTION_DOWN):
touchX = (int) motionEvent.getX();
touchY = (int) motionEvent.getY();
move = true;
return true;
default:
return super.onTouchEvent(motionEvent);
}
}
}
Gonna need to be an x+=speedX or y+=speedY in there somewhere so that the x or y value changes causing the ball to move. If your trying to implement some physics in there ur gonna need some maths.
You need a way to change the (x, y) values at a certain interval. You can do this with the Timer class, but then you get into some difficult multithreading code. I suggest looking at using OpenGL ES or a higher level library such as LibGDX. Both of these provide an event loop which allow you to update objects that will be drawn.
Basically you want to move the circle to the place the user touched the screen, using some type of constant speed?
If all you want is to move a circle the Timer can do it - or you could use something like: https://github.com/MasayukiSuda/FPSAnimator
Finally, are you always looking for a linear straight line constant speed movement? Then your math is fine. If your looking for something with gravity etc. then you can reference this: https://developer.android.com/guide/topics/graphics/physics-based-animation.html and consider this: https://github.com/google/liquidfun
This will move the circle exactly to the point the user touched:
theta = atan2(touchY - y,touchX - x)
speedX = cos(theta)
speedY = sin(theta)
x += speedX
y += speedY
What happens once the circle reaches the point depends on how the calculation is implemented. If you want the circle to continue on its path and travel the direction infinitely, you must only calculate the velocity of x and y once. That way the same x and y velocity will always be used to update the position of the circle.
If the velocities are recalculated every time the position of the circle is to be updated, the circle will continuously move to the point even once it has technically reached it and must manually be stopped.
The velocities can be increased by multiplying them by a value greater than 1. To maintain the correct direction, the value should be the same for both velocities.
Example:
theta = atan2(touchY - y,touchX - x)
speedX = maxSpeed*cos(theta)
speedY = maxSpeed*sin(theta)
x += speedX
y += speedY

How to move a Path on a Canvas in Android?

I am trying to make an Android paint application for finger painting and I am having trouble with moving the lines I draw.
What I tried to do was offset the path of the currently selected line by the difference between the initial finger press coordinates and the current coordinates in OnTouchEvent during ACTION_MOVE.
case MotionEvent.ACTION_MOVE:
selectline.getLine().offset(x - otherx, y - othery);
otherx and othery are set as the x and y coordinates during ACTION_MOVE and x and y are the current cursor coordinates. My lines are stored as a separate class containing the path, color, thickness and bounding box.
What I got was the shape flying off the screen in the direction of my finger without stopping at the slightest movement. I tried using a matrix to move the path, but the result was the same.
When I tried to insert a "do while" that would check whether the current coordinates would match the path's .computeBounds() rectangle center, but the program crashes as soon as I move my finger.
Any help would be appreciated, thanks.
Most likely that you did not use the right scale for the coordinates.
Source: Get Canvas coordinates after scaling up/down or dragging in android
float px = ev.getX() / mScaleFactor + rect.left;
float py = ev.getY() / mScaleFactor + rect.top;
// where mScaleFactor is the scale use in canvas and rect.left and rect.top is the coordinate of top and left boundary of canvas respectively
Its a bit late but it may solve others problem. I solved this issue like this,
get initial X,Y position on onLongPress
public void onLongPress(MotionEvent motionEvent) {
try {
shapeDrag = true;
SmEventX = getReletiveX(motionEvent);
SmEventY = getReletiveY(motionEvent);
} catch (Exception e) {
e.printStackTrace();
}
and then on onToucn(MotionEvent event)
case MotionEvent.ACTION_MOVE: {
actionMoveEvent(motionEvent);
try {
if (shapeDrag) {
StylePath sp = alStylePaths
.get(alStylePaths.size() - 1);
Path mpath = sp.getPath();
float tempX = getReletiveX(motionEvent) - SmEventX;
float tempY = getReletiveY(motionEvent) - SmEventY;
mpath.offset(tempX, tempY);
SmEventX = getReletiveX(motionEvent);
SmEventY = getReletiveY(motionEvent);
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
I faced the same trouble and in my case it was a very naive mistake. Since the description of the "symptoms" matches exactly (shape flying off the screen in the direction of the finger at the slightest movement, shape moved correctly at ACTION_UP event), I think the reason behind might be the same.
Basically the problem is in the update of the touch position coordinates within the ACTION_MOVE event. If you don't update the last touch position, the calculated distance will be always between the current touch position and the first touch position stored at ACTION_DOWN event: if you apply this offset consecutively to the path, the translation will sum up and consequently the shape will "fly" rapidly off the screen.
The solution is then quite simple: just update the last touch position at the end of the ACTION_MOVE event:
float mLastTouchX, mLastTouchY;
#Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
// get touch position
final float x = ev.getX();
final float y = ev.getY();
// save the initial touch position
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_MOVE: {
// get touch position
final float x = ev.getX();
final float y = ev.getY();
// calculate the distance moved
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
// here apply translation to the path
// update touch position for the next move event
mLastTouchX = x;
mLastTouchY = y;
break;
}
}
return true;
}
Hope this helps.

How to find if ACTION_MOVE touch event is on a circular path: Android

I am moving an image around a circle via touch events.
I want the user to touch the image and when the user drags this image around the cirle, it moves, otherwise it doesn't move.
Could someone please help with the math on how to check if the finger in moving along the circle or not and them move the image accordingly.
Thanks.
UPDATE:
I am trying to revolve an image around a circle. It is already placed on the circle edge.
But on touch and move actions it takes it self as the centre and then starts moving around a defined radius.
could someone pls see the code and let me know where i am going wrong.
Thanks.
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialX = event.getX();
mInitialY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
mEndX = event.getX();
mEndY = event.getY();
float deltaX = mEndX - mInitialX;
float deltaY = mEndY - mInitialY;
double angleInDegrees = Math.atan(deltaY / deltaX) * 180 / Math.PI;
mInitialX = mEndX;
mInitialY = mEndY;
mCurrTempIndicator.setRotation((float)angleInDegrees);
mCurrTempIndicator.setTranslationX((float)(310*(Math.cos(angleInDegrees))));
mCurrTempIndicator.setTranslationY((float)(310*(Math.sin(angleInDegrees))));
break;
case MotionEvent.ACTION_UP:
allowRotating = true;
break;
}
return true;
}
float dx = event.getX() - circleCenterX
float dy = event.getY() - circleCenterY;
// r is now the radius of the touch event, you can compare it with the radius of your circle to find out if it's close enough
float r = FloatMath.sqrt((dx * dx) + (dy * dy));
if(r > circleRadius - 10 && r < circleRadius + 10){
// a is now the angle between the center point and the touch point in radians. With 0 being 3 o'clock, -/+PI being 9 o'clock -PI/2 at 12 o'clock and +PI/2 at 6 o'clock.
float a = Math.atan2(dy, dx);
}
Adding to the above answer. I made some changes to make it work properly. In this I'm making an ImageView to revolve around a fixed center point meaning ImageView will move along the circumference of the circle.
double centerX, centerY; //Center coordinates of the circle
int radius; //Set the radius in pixels
//The view which will revolve on the circumference of the circle
imageView = new ImageView(this);
imageView.setBackgroundResource(android.R.drawable.ic_menu_add); //Set your view icon
imageView.setPadding(10, 10, 10, 10);
relativeLayout.addView(imageView);
//Setting the imageView on the circumference of the circle
imageView.setTranslationX(centerX + radius);
imageView.setTranslationY(centerY);
//Add imageview to the parent layout dynamically(choice)
relativeLayout.addView(imageView);
imageView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_MOVE:
double dx = motionEvent.getRawX() - centerX;
double dy = motionEvent.getRawY() - centerY; //Also subtract toolbar height and status bar height if applicable
//Distance between user's point of touch and circle center
double distance = Math.sqrt((dx * dx) + (dy * dy));
if (distance > radius - 200 && distance < radius + 200) {
double angle = Math.atan2(dy, dx);
//Getting the point on the circumference of the circle using the angle and the radius
double x = centerX + radius * Math.cos(angle);
double y = centerY + radius * Math.sin(angle);
//Finally move the view along the circimference of the circle as the user drags the view
imageView.animate().y((float) y).x((float) x).setDuration(0).start();
}
break;
}
return true;
}
});

How to keep a dot drawn on canvas at a fixed point on screen, even when the canvas is pinch zoomed? - Android

I have a background image as a drawable in my custom view. This drawable may be pinch zoomed or moved.
Currently I need a green dot that is drawn on the image to be stationary relative to the screen. That is, it should be always at the same position with the pin as shown below. (Of course, the pin is simply an ImageView and does NOT move at all!)
I have successfully made it stationary relative to the screen, when the map behind is moved as follows in my custom view, MapView:
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: { // triggered as long as finger movers
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
// update the starting point if the 'Start' button is not yet pressed
// to ensure the screen center (i.e. the pin) is always the starting point
if (!isStarted) {
Constant.setInitialX(Constant.INITIAL_X - dx);
Constant.setInitialY(Constant.INITIAL_Y - dy);
if ((historyXSeries.size() > 0) && (historyYSeries.size() > 0)) {
// new initial starting point
historyXSeries.set(0, Constant.INITIAL_X);
historyYSeries.set(0, Constant.INITIAL_Y);
}
}
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
By doing that above, my green dot stays there, when the background image is moved.
But I have problems in trying to make it stay there, when the background image is zoomed.
Essentially, I don't really understand how canvas.scale(mScaleFactor, mScaleFactor) works, and therefore I cannot move the green dot accordingly like what I have done in the simple moving case.
I think something should be added in the scale listener handler below, could anybody help me fill that part?
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(1f, Math.min(mScaleFactor, 10.0f)); // 1 ~ 10
// HOW TO MOVE THE GREEN DOT HERE??
invalidate();
return true;
}
Or please at least explain how canvas.scale(mScaleFactor, mScaleFactor) works, and how may I move the green dot accordingly?
Keep in mind that the canvas is thought to scale everything according to the scale factor, so while going against the zoom is possible, it is probably not the best approach. However, if this is what you're looking for, I will help you as best as I can.
I am assuming the following:
Scale factor is relative to the current zoom (old zoom is always scale factor 1). If this is not the case, then you should observe the zoom values after scaling roughly 200% two times and seeing if the resulting scale factor is 4 or 3 (exponential or linear). You can achieve the results below by normalizing the scale factor to 2 for a zoom factor of 200%, for example. You'll have to remember the old scale factor in order to do so.
No rotation is performed
If this is the case then following can be said for a marker with respect to the zoom center.
For every horizonal pixel x away from the zoom center after zoom, its original position could be calculated to be: zoom_center_x + *x* / scale_factor (or alternatively zoom_center_x + (marker_x - zoom_center_x) / scale_factor). In other words, if zoom center is (50, 0) and the marker is (100, 0) with a scale factor of 2, then the x position of the marker prior to the zoom was 50 + (100 - 50) / 2 or 75. Obviously, if the marker is in the same position of the zoom center, then the x position will be the same as the zoom center. Similarly, if the scale is 1, then the x position for the marker will be the same as it is now.
The same can be applied to the y axis.
While I can't know exactly how to set the position of your marker, I would expect the code to look something like:
Point zoomCenter = detector.getZoomCenter();
// Set marker variable here
marker.setX(Math.round(zoomCenter.getX() + ((double)(marker.getX() - zoomCenter.getX())) / mScaleFactor));
marker.setY(Math.round(zoomCenter.getY() + ((double)(marker.getY() - zoomCenter.getY())) / mScaleFactor));
I hope that helps.

Multiple touch events

I want to have four different areas on the screen and be able to determine if each of those areas are touched. Every area should have a corresponding boolean value that is true if touched. When one area is true that part of the canvas will become a different color.
It is very important that each area works independently, so if area 1 and two are true and the user lets go of area 1 it will instantly become false without affecting area 2.
Thanks!
Edit:
I've tried so many things but I've just started over again. This is from a class that has a SurfaceView with a canvas. I can't figure out what goes where.
public boolean onTouch(View v, MotionEvent ev) {
// TODO Auto-generated method stub
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
x = ev.getX();
y = ev.getY();
if (canvasHeight != 0 && canvasWidth != 0) {
if (x < canvasWidth/2 && y < canvasWidth/2){
x1 = x;
y1 = y;
}
if (x < canvasWidth && y > canvasHeight){
x2 = x;
y2 = y;
}
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
Here's how I'd approach your problem:
Create rects that define the four regions of the screen that can be
pressed.
Using multi-touch, check if the coordinated are in any of the defined
rects. If this is the case, then set the boolean value of that region
of the screen to true, so a color will be rendered to that region. If
the coordinate is not in the rect, then set the boolean to false.
I hope this gets you started!
Update:
I suggest you start off simple and don't use multitouch. In you touch method you can get the x and y value of the touch. Once the screen is touched you could call a method like this with x and y being your parameters eg. checkRegion(x,y).
The method could return an int of the region (since in this case you can only touch one at a time):
public int checkRegion(int x, int y) {
int clickedRegion;
// Some code that will return the region number: 1 = top left, 2 = top right, 3 = bottom left, 4 = bottom right
return clickedRegion;
}
First off, you need to check all four areas;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
x = ev.getX();
y = ev.getY();
if (canvasHeight != 0 && canvasWidth != 0) {
//you need four sections, not the two?
if (x < canvasWidth/2 && y < canvasWidth/2){
//set your respective canvas color to what you want it for this quadrant
}
if (x < canvasWidth/2 && y > canvasWidth/2){
//set your respective canvas color to what you want it for this quadrant
}
if (x < canvasWidth && y > canvasHeight){
//set your respective canvas color to what you want it for this quadrant
}
if (x < canvasWidth && y < canvasHeight){
//set your respective canvas color to what you want it for this quadrant
}
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
Now, assuming you can get that to work, all you need to do is do it again in the action_pointer_down and it will register as a "secondary" click.
If the action is Action_up, just figure out where the user liftes his finger and do the same thing. One issue you should think about is this; what if a user clicks in Quadrant 1, but lifts it up in quadrant 3? That is a slightly more complicated condition, but I think you can skip it for now.

Categories

Resources