Android- How to drag a picture on a canvas - java

I need to drag an image like in a videogame. I have a few lines drawed and I need to drag it and see the lines drawed before "out" of the window. I give you two images trying to help to understand it.
Image before drag:
Then I touch the screen with my finger and I make a move to the top of the screen, so this three lines have to be upper than the initial image, like this:
I don't know how to do this "drag efect", can someone help me? This is not an image as a .jpg file, is three lines drawed with "drawLine" on a canvas into an imageView.
My onTouchEvent Code and my drawRectangle code:
public boolean onTouch(View view, MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_UP:
{
x = event.getX();
y = event.getY();
getFirstFoot();
return true;
}
case MotionEvent.ACTION_MOVE:
{
ammountX = event.getX() - startX;
ammountY = event.getY() - startY;
canvas.save();
canvas.translate(ammountX, ammountY);
//canvas.restore();
imageView.invalidate();
return true;
}
case MotionEvent.ACTION_DOWN:
{
startX = event.getX();
startY = event.getY();
}
}
return true;
}
drawRectagle code:
private void drawRectangle(boolean erase)
{
buttonRed.setVisibility(View.VISIBLE);
buttonGree.setVisibility(View.VISIBLE);
buttonYellow.setVisibility(View.VISIBLE);
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.ascensorsininfo);
canvas.drawBitmap(bitmap2, x - (bitmap2.getWidth() / 2), y - bitmap2.getHeight(), new Paint());
drawedPoints.add(String.valueOf(x - (bitmap2.getWidth() / 2)));
drawedPoints.add(String.valueOf(y - bitmap2.getHeight()));
imageView.invalidate();
y -= bitmap2.getHeight();
}
Any solution would be apreciated. Thanks!

Check out Canvas.translate
It will "move" everything you draw afterwards the ammount you say in x and y axis.
Don't forget to save and restore it, like:
canvas.save();
canvas.translate(ammountX, ammountY);
// [draw your stuff here]
canvas.restore();
If your question is more towards how to detect the dragging effect, you have to Override your view's onTouchEvent method. Something like:
#Override
public boolean onTouchEvent (MotionEvent event){
if(event.getActionMasked() == MotionEvent.DOWN){
startX = event.getX();
startY = event.getY();
return true;
}else if(event.getActionMasked() == MotionEvent.MOVE){
ammountX = event.getX() - startX;
ammountY = event.getY() - startY;
return true;
}
return super.onTouchEvent(event);
}

Related

Dragging a rotated view by adjusting layout params

I am trying to make a view follow my finger and do some rotation and scaling in multitouch with the following code
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
final int action = motionEvent.getActionMasked();
int newPosX,newPosY;
switch (action) {
case MotionEvent.ACTION_DOWN: {
final int pointerIndex = motionEvent.getActionIndex();
final float x = motionEvent.getX( pointerIndex);
final float y = motionEvent.getY( pointerIndex);
RelativeLayout.LayoutParams parms = (RelativeLayout.LayoutParams) view.getLayoutParams();
// Remember where we started (for dragging)
mLastTouchX = (int) x;
mLastTouchY = (int) y;
// Save the ID of this pointer (for dragging)
mActivePointerId = motionEvent.getPointerId( 0);
break;
}
case MotionEvent.ACTION_MOVE: {
if(motionEvent.getPointerCount()==2){
float newDist = spacing(motionEvent);
float scale = newDist / oldDist * view.getScaleX();
view.setScaleY(scale);
view.setScaleX(scale);
float newAngle = rotation(motionEvent);
float a = newAngle - oldAngle;
view.animate().rotationBy(a).setDuration(0).setInterpolator(new LinearInterpolator()).start();
}
// Find the index of the active pointer and fetch its position
final int pointerIndex =
motionEvent.findPointerIndex( mActivePointerId);
final float x = motionEvent.getX( pointerIndex);
final float y = motionEvent.getY( pointerIndex);
// Calculate the distance moved
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
layoutParams.leftMargin += dx;
layoutParams.topMargin += dy;
view.setLayoutParams(layoutParams);
break;
}
case MotionEvent.ACTION_POINTER_DOWN:{
oldDist = spacing(motionEvent);
oldAngle = rotation(motionEvent);
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = motionEvent.getActionIndex();
final int pointerId = motionEvent.getPointerId( pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = (int) motionEvent.getX( newPointerIndex);
mLastTouchY = (int) motionEvent.getY( newPointerIndex);
mActivePointerId = motionEvent.getPointerId( newPointerIndex);
}
break;
}
}
return true;
}
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
private float rotation(MotionEvent event) {
double delta_x = (event.getX(0) - event.getX(1));
double delta_y = (event.getY(0) - event.getY(1));
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}
Everything is working well until the view is rotated. When it is rotated above 90 degrees and we try to drag it it's jumping around the touch point. I think it has something to do with
layoutParams.leftMargin += dx;
layoutParams.topMargin += dy;
setLayoutParams(layoutParams);
I am working to make this work for last two days with no success.
NOTE: what I am trying to achieve is to make a view drag rotate and scale with 2 fingers(drag with single finger also)
I followed the drag code from Google's documentation so as to make it not jump while switching fingers.
I am using this to rotate
view.animate().rotationBy(a).setDuration(0).setInterpolator(new LinearInterpolator()).start();
because when i use view.setRotate(),the view is vibrating.
EDIT 1:
I removed
layoutParams.leftMargin += dx;
layoutParams.topMargin += dy;
and replaced it with:
//To get the moved distance of the finger(X and Y)
float diffX = motionEvent.getX(pointerIndex) - mLastTouchX;
float diffY = motionEvent.getY(pointerIndex) - mLastTouchY;
//to get the distance from touch point and the top or left of the view
final float dx = motionEvent.getRawX() - (motionEvent.getRawX()-motionEvent.getX());
final float dy = motionEvent.getRawY() - (motionEvent.getRawY()-motionEvent.getY());
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
//Settings appropriate value for the margin top and margin left.
layoutParams.leftMargin = (int) ((( motionEvent.getRawX() )-dx )+ diffX );
layoutParams.topMargin = (int) ((( motionEvent.getRawY() )-dy )+ diffY );
Now even after rotating the view is not moving around. but when i switch the active finger its jumping around from the actual position.
On ACTION_POINTER_UP i am doing this to change shift the active finger
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = motionEvent.getActionIndex();
final int pointerId = motionEvent.getPointerId( pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
Log.d(TAG,"New pointer index :"+newPointerIndex);
mLastTouchX = (int) motionEvent.getX( newPointerIndex);
mLastTouchY = (int) motionEvent.getY( newPointerIndex);
mActivePointerId = motionEvent.getPointerId( newPointerIndex);
}
break;
}
Normally I would advise against manipulating the view's position and size excessively on runtime, it updates the layout needlessly lowers performance, hard to control and rarely looks good.
Instead, if you don't want to delve into OpenGl, then my advice would be a Custom View, create a class that extend View which you later add in your layout XML, <com.packagename.MyCustomView ... />.
this view will have the size of your whole screen.
You #Override the onDraw(Canvas canvas) method, it will give you the canvas of the view, you can use the Canvas to draw graphics in the View.
write invalidate(); in the end of you onDraw() to tell the View to try and call onDraw() again when available, you should stop calling it once user stops touching your view so add some boolean to it.
now the touches, you can register to touch events like you did but inside your custom view and use the x,y and the size of your view from the canvas.getWidth() and canvas.getHeight() to determine what to do.
if you need the size of the view before you reach onDraw() override onMeasure().
Summery
This may not look that easy but it is, and it's fast and quit efficient, the graphics will be smooth, if you want the best possible outcome and are not afraid of lots of code then you can look into drawing using OpenGl but I wouldn't advise you that for what your trying to do.
Hope I understood the problem and helped you - Good Luck!

Java Android onTouch Direction

I am trying to develop a simple game wherein I need to throw a bullet .
I would like the ball follows the touch, or swipe direction.
However, I have a small problem that I have not had success in solving.
I take coordinates on the ACTION_DOWN of MotionEvent, and the coordinates on the ACTION_UP of MotionEvent, then calculate the straight line equation in order to move the ball according to this straight line.
The problem is that when I swipe in a direction close to "up" or "forward" or "in front of", the ball moves at lightning speed while in the other direction of more skewed (right or left) or diagonal, the ball moves at a normal speed.
Where is the flaw in my calculation?
Help me please, I am not far from the result but need to you for solve this problem!
The onTouch method:
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()) {
case(MotionEvent.ACTION_DOWN):
balle.setX(event.getX());
balle.setY(event.getY());
ya = event.getY();
xa = event.getX();
break;
case(MotionEvent.ACTION_UP):
xb = event.getX();
yb = event.getY();
balle.setMove(true);
break;
}
return true;
}
This is my moveDirection method:
public void moveDirection(float xa, float ya, float xb, float yb) {
if(!move) {
return;
}
else {
float m, b;
float dx = xb-xa;
m = (yb - ya) / (xb - xa);
b = ya - (m * xa);
if(dx > 0) {
this.x += speedX;
}
else {
this.x += -speedX;
}
this.y = (m * this.x + b);
}
}
Thank you in advance!
I see you are not capturing all the events so, maybe the code from the Android Documentation with de VelocityTracker api may help, in this example when you pull down your finger, a new tracker is created and when you move (in any direction) you capture the velocity of the event, i think you can move the ball according with the velocity and direction of touch.
public class MainActivity extends Activity {
private static final String DEBUG_TAG = "Velocity";
...
private VelocityTracker mVelocityTracker = null;
#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.
Log.d("", "X velocity: " +
VelocityTrackerCompat.getXVelocity(mVelocityTracker,
pointerId));
Log.d("", "Y velocity: " +
VelocityTrackerCompat.getYVelocity(mVelocityTracker,
pointerId));
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// Return a VelocityTracker object back to be re-used by others.
mVelocityTracker.recycle();
break;
}
return true;
}
}
Link for doc.

How to draw a circle with Bitmap pixels?

In my app I blend 2 Bitmaps, one Bitmap is on the screen and when i Swipe with my finger it reveals the matching pixels from the second Bitmap. The way I have now works but when I touch the screen it only reveals squares from the other Bitmap since pixels are squares, and I'm revealing a group of them by touching the screen.
How do I group pixels together to create a circle?
This is my current code:
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (!surfaceReady)
return false;
// Check if the touch pointer is the one you want
if (event.getPointerId(event.getActionIndex()) == 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// User touched screen...
case MotionEvent.ACTION_MOVE:
// User dragged his finger...
blend((int) event.getX(), (int) event.getY());
}
// Update the blending effect bitmap here and trigger a frame
// redraw,
// if you don't already have an animation thread to do it for you.
drawOverlays();
return true;
}
return false;
}
And the blending code:
// In order to make the effect more "visible" I've created a larger brush
public int BS = 50;
public int HBS = BS >> 1;
int[] pixels = new int[BS * BS];
private void blend(int x, int y) {
if (x >= HBS && y >= HBS && x < bmp2.getWidth() - HBS && x < operation.getWidth() - HBS && y < bmp2.getHeight() - HBS && y < operation.getHeight() - HBS) {
bmp2.getPixels(pixels, 0, BS, x - HBS, y - HBS, BS, BS);
operation.setPixels(pixels, 0, BS, x - HBS, y - HBS, BS, BS);
}
}

how do i change specific rect color on a canvas in android?

ive been trying to change the color of specific rects in an array of rects when i touch the screen but it doesnt seem to be working, heres my code:
public Paint blue = new Paint();
RandomColorGen rc;
ArrayList<Integer> colors = RandomColorGen.ColorList(5);
Random rand = new Random();
int columns = 50;
int rows = 50;
Rect square[][] = new Rect[rows][columns];
public boolean isTouched;
public Canvas canvas;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
this.canvas = canvas;
for (int x = 0; x < rows; x++) {
for (int y = 0; y < columns; y++) {
square[x][y] = new Rect();
blue.setColor(colors.get(rand.nextInt(colors.size())));
blue.setStyle(Paint.Style.FILL);
square[x][y].set(0, 0, (canvas.getWidth() - 10) / rows,
((canvas.getHeight() - 100) / columns));
square[x][y].offsetTo(x * ((canvas.getWidth() - 10) / rows), y
* ((canvas.getHeight() - 100) / columns));
canvas.drawRect(square[x][y], blue);
}
}
if(isTouched){
blue.setColor(colors.get(rand.nextInt(colors.size())));
blue.setStyle(Paint.Style.FILL);
canvas.save();
canvas.clipRect(square[1][1]);
canvas.drawRect(square[1][1], blue);
canvas.restore();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isTouched = true;
break;
}
return true;
}
the colors.get() thingy is an arraylist of colors. am i taking the wrong approach?
How you are calling the paint function after the onTouch action performed...
OnTouch action is the first action so how do you call Paint() function??
I just tested your code, it works but there are couple of notes worth mentioning:
Allocating objects during drawing is a very bad behavior ( specially when allocating 50*50 ) ! consider moving the allocation code to the constructor and change position of the rectangles at the onDraw() method if you want to achieve the same behavior you have now.
Your usage for onTouchEvent() is not complete, you need to have the isTouched true as long as the user didn't raise his hand, which can be done as the following :
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isTouched = true;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
isTouched = false;
break;
}
invalidate();
return true;
}
Also request a layout every time you receive a TouchEvent by using invalidate()

Touch within view only- android

I am trying to do this but it gave me headache
I have a view that draw a color bar. And I want to allow user to move it around (drag and drop)
this is on draw method:
int r,g,b;
for(int i = 0; i < 64; i++)
{
r = Math.round((float)colorBuffer[i][0]*MAX_SCALING);
g = Math.round((float)colorBuffer[i][1]*MAX_SCALING);
b = Math.round((float)colorBuffer[i][2]*MAX_SCALING);
paint.setColor(Color.argb(MAX_SCALING, r, g, b));
canvas.drawLine(x1, y1, x2, y1, paint);
y1+=3;
}
rgb is just the color.
this is my ontouch implementation.
public boolean onTouchEvent(MotionEvent e)
{
float x = e.getX();
float y = e.getY();
switch (e.getAction())
{
case MotionEvent.ACTION_DOWN:
{
Toast t = Toast.makeText(getContext(), "Touch", Toast.LENGTH_SHORT);
t.show();
break;
}
case MotionEvent.ACTION_MOVE:
{
x1=(int) x;
x2=(int) (x+30);
y1=(int) y;
break;
}
case MotionEvent.ACTION_UP:
{
Toast t = Toast.makeText(getContext(), "Release", Toast.LENGTH_SHORT);
t.show();
x1=(int) x;
x2=(int) (x+30);
y1=(int) y;
break;
}
}
invalidate();
return true;
}
this works perfectly fine, but the problem is that it works even outside the view.
so if i touch outsite the view my bar move over there too
even when i click a button my bar try to move too
I want to drag and drop the component only if the user touch within the view
any help would be appreciate
thanks
On touch get the x, y, width, and height of your view. Then every time when you are moving the bar, as I understand in MotionEvent.ACTION_MOVE, check if current coordinates are whithin the views's bounds, if this is true, then allow to move the bar.

Categories