Ok, I'm trying to make a program that utilizes a linedrawview. When the user starts a touch event(action DOWN), it gets the current x and y and stores them in variables. Then when the user drags their finger around, a line is drawn and animates in a rubber band way. Finally, when the user lets go (action UP), the line is created. I'm having a lot of trouble with this and would like some assistance. My code so far for LineDrawView.java:
// Project: Java2LineDrawEx
// File: LineDrawView.java
// Date: 4/9/13
// Author: Joshua Lefelhocz
// Description: custom view to draw lines on
package com.lcc.java2lab11lefelhocz;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;
// Notice this class extends View
public class LineDrawView extends View
{
// This view's bounds
private int xMin = 0;
private int xMax;
private int yMin = 0;
private int yMax;
private float currentX;
private float currentY;
// Paint object
private Paint paintFill;
// constructor
public LineDrawView(Context context)
{
// call the super class constructor
super(context);
// The Paint class holds the style and color information about how to draw geometries, text and bitmaps.
// For efficiency create the paint objects in the constructor, not in draw
// paint.setStrokeWidth(10); // works on lines
// You can change the color of Paint without effecting objects already drawn
// You can NOT change the style of Paint without effecting objects already drawn
// The Style, TextSize apply to all objects drawn with the paint.
// Create a default Paint object Style=Fill
paintFill = new Paint();
// set the background color when the view is created
this.setBackgroundColor(Color.LTGRAY);
}
// Called to draw the view. Also called by invalidate().
#Override
protected void onDraw(Canvas canvas)
{
// canvas.drawColor(Color.LTGRAY); // this works in onDraw to set the background color
// Draw a Red Diagonal line from upper left corner to bottom right corner
paintFill.setColor(Color.RED);
canvas.drawLine(xMin, yMin, xMax, yMax, paintFill);
// draw a blue line 10 pixels wide horizontal across the center.
paintFill.setColor(Color.BLUE);
paintFill.setStrokeWidth(10);
canvas.drawLine(xMin, yMax/2, xMax, yMax/2, paintFill);
// draw a yellow line 20 pixels wide vertical across the center.
paintFill.setColor(Color.YELLOW);
paintFill.setStrokeWidth(20);
canvas.drawLine(xMax/2, yMin, xMax/2, yMax, paintFill);
}
// Called when the view is first created or its size changes.
#Override
public void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
{
// Set the view bounds
xMax = width-1;
yMax = height-1;
}
public boolean onTouchEvent(MotionEvent event)
{
currentX = event.getX();
currentY = event.getY();
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
float startX = currentX;
float startY = currentY;
case MotionEvent.ACTION_MOVE:
float endX =
case MotionEvent.ACTION_UP:
return super.onTouchEvent(event);
}
return super.onTouchEvent(event);
}
}
Okay, I found out: Make sure to set the start position x and y, like this:
currentXExample = event.getX();
currentYExample = event.getY();
exampleStartX = currentXExample;
exampleStartY = currentYExample;
exampleEndX = currentXExample;
exampleEndY = currentYExample;
You can leave it out of the Up event, but never leave it out of the MOVE event or it will not work.
LineDrawView.java
// Project: Java2Lab
// File: LineDrawView.java
// Date: 4/9/13
// Author: Joshua Lefelhocz
// Description: custom view to draw lines on
package com.lcc.java2lab11lefelhocz;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;
// Notice this class extends View
public class LineDrawView extends View
{
// This view's bounds
private int xMin = 0;
private int xMax;
private int yMin = 0;
private int yMax;
private float currentX;
private float currentY;
private float startX;
private float endX;
private float startY;
private float endY;
// Paint object
private Paint paintFill;
// constructor
public LineDrawView(Context context)
{
// call the super class constructor
super(context);
// The Paint class holds the style and color information about how to draw geometries, text and bitmaps.
// For efficiency create the paint objects in the constructor, not in draw
// paint.setStrokeWidth(10); // works on lines
// You can change the color of Paint without effecting objects already drawn
// You can NOT change the style of Paint without effecting objects already drawn
// The Style, TextSize apply to all objects drawn with the paint.
// Create a default Paint object Style=Fill
paintFill = new Paint();
// set the background color when the view is created
this.setBackgroundColor(Color.LTGRAY);
}
// Called to draw the view. Also called by invalidate().
#Override
protected void onDraw(Canvas canvas)
{
//canvas.drawColor(Color.LTGRAY); // this works in onDraw to set the background color
// Draw a Red Diagonal line from upper left corner to bottom right corner
paintFill.setColor(Color.BLACK);
canvas.drawLine(startX, startY, endX, endY, paintFill);
}
// Called when the view is first created or its size changes.
#Override
public void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
{
// Set the view bounds
xMax = width-1;
yMax = height-1;
}
public boolean onTouchEvent(MotionEvent event)
{
currentX = event.getX();
currentY = event.getY();
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
startX = currentX;
startY = currentY;
return true;
case MotionEvent.ACTION_MOVE:
endX = currentX;
endY = currentY;
invalidate();
return true;
case MotionEvent.ACTION_UP:
return true;
}
return super.onTouchEvent(event);
}
}
Related
I need to get the touch x and y with respect to the canvas to check for collisions and things like that after I have moved and scaled the canvas.
I already managed to get the touch coordinate whenever I translate the canvas or scale it around the origin (0,0) by using the following code:
private float convertToCanvasCoordinate(float touchx, float touchy) {
float newX=touchx/scale-translatex;
float newY=touchy/scale-translatey
}
But if I scale the canvas around another point like for example canvas.scale(scale,scale,50,50), it doesn't work .
I know it shouldn't work but I just couldn't figure out how to solve it. I already looked at other questions but none of the answers talks about how to get the coordinate if I scale according to a specific point.
The most basic way to properly do a scene in android is to use a matrix to modify the view and the inverse of that matrix to modify your touches. Here's a simplified answer. Kept very short.
public class SceneView extends View {
Matrix viewMatrix = new Matrix(), invertMatrix = new Matrix();
Paint paint = new Paint();
ArrayList<RectF> rectangles = new ArrayList<>();
RectF moving = null;
public SceneView(Context context) { super(context); }
public SceneView(Context context, AttributeSet attrs) { super(context, attrs); }
public SceneView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
#Override
public boolean onTouchEvent(MotionEvent event) {
//transform touch. (inverted matrix)
event.transform(invertMatrix);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
moving = null;
//collision detection
for (RectF f : rectangles) {
if (f.contains(event.getX(), event.getY())) {
moving = f;
return true;
}
}
// adding arbitrary transforms.
viewMatrix.postTranslate(50,50);
viewMatrix.postScale(.99f,.99f);
viewMatrix.postRotate(5);
// inverse matrix is needed for touches.
invertMatrix = new Matrix(viewMatrix);
invertMatrix.invert(invertMatrix);
break;
case MotionEvent.ACTION_MOVE:
if (moving != null) {
moving.set(event.getX() - 50, event.getY() - 50, event.getX() + 50, event.getY() + 50);
}
break;
case MotionEvent.ACTION_UP:
if (moving == null) {
rectangles.add(new RectF(event.getX() - 50, event.getY() - 50, event.getX() + 50, event.getY() + 50));
}
break;
}
invalidate();
return true;
}
#Override
protected void onDraw(Canvas canvas) {
// transform the view by the matrix.
canvas.concat(viewMatrix);
// draw objects
for (RectF f : rectangles) {
canvas.drawRect(f,paint);
}
}
This is rather minimalist, but it shows all the relevant aspects.
Moving the view
Touch modification
Collision detection.
Each time you touch the screen it will move diagonally, zoomout, and rotate (basically moves in a spiral), and create a black rectangle. If you touch the rectangles you can move them around to your heart's content. When you click the background, more spiraling the view, dropping black rectangles.
See: https://youtu.be/-XSjanaAdWA
The other way does not work. You could, in theory, take the scene we want and convert that via the View class rather than in the canvas. This would make the touch events occur in the same space as the screen. But Android will void out touch events that occur outside of the view, So MotionEvents that begin outside of the original clipped part of the view will be discarded. So this is a non-starter. You want to transform the canvas, and counter transform the MotionEvents.
private float convertToCanvasXCoordinate(float touchx,float offsetx,float viewportVisibleWidth){
float newx=(touchx*viewportVisibleWidth)/getWidth()+offsetx;
return newx;
}
private float convertToCanvasYCoordinate(float touchy,float offsety,float viewportVisibleHeight){
float newy=(touchy*viewportVisibleHeight)/getHeight()+offsety;
return newy;
}
i just found out there is a function canvas.getClipBound() which is a rectangle representing the visible viewport that includes the offsetx offset y (the left and top of the rectangle respectively) and the viewport width and height
simply call these functions and it will get you touchx and touchy with respect to canvas
This should help:
float px = e.getX() / mScaleFactorX;
float py = e.getY() / mScaleFactorY;
int ipy = (int) py;
int ipx = (int) px;
Rect r = new Rect(ipx, ipy, ipx+2, ipy+2);
And where the canvas is:
final float scaleFactorX = getWidth()/(WIDTH*1.f);
final float scaleFactorY = getHeight()/(HEIGHT*1.f);
if(mScaleFactorX == INVALID){
mScaleFactorX = scaleFactorX;
mScaleFactorY = scaleFactorY;
}
This is a really simple way, and it works because it scales down the onTouch coordinates to be the same min/max as the canvas, causing them to be scaled. Do NOT use getRawX and getRawY because that will return wrong coordinates if you are using a custom layout with the view added and other elements around it. getX and getY returns the accurate coordinates scaled to your view.
This is really simple and does not take up a lot of code. scaleFactor can be used elsewhere to handle zoom(you take care of that code) but what this code does is to handle the issue of getting the pointer coordinates to match the scaled canvas
I have a Canvas that is scaled so everything fits better:
#Override
public void draw(Canvas c){
super.draw(c);
final float scaleFactorX = getWidth()/(WIDTH*1.f);
final float scaleFactorY = getHeight()/(HEIGHT*1.f);
if(c!=null) {
final int savedState = c.save();
c.scale(scaleFactorX, scaleFactorY);
(rendering)
c.restoreToCount(savedState);
}
}
It scales based on these two:
public static final int WIDTH = 856;
public static final int HEIGHT = 1050;
Which causes the problem that the coordinates of the MotionEvent that handles touch events is not equal to the coordinates that is created with the Canvas. This causes problems when I try to check collision between the MotionEvent Rect and the Rect of a class that is based on the rendering scale. This causes the class SuperCoin's X coordinate to not be equal to MotionEvent X coordinates.
Usually, MotionEvent's coordinates, both X and Y is way bigger than the screen's max size(defined by WIDTH and HEIGHT)
#Override
public boolean onTouchEvent(MotionEvent e) {
super.onTouchEvent(e);
switch (MotionEventCompat.getActionMasked(e)) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
(...)
Rect r = new Rect((int)e.getX(), (int)e.getY(), (int)e.getX() + 3, (int)e.getY() + 3);
if(superCoins.size() != 0) {
for (SuperCoin sc : superCoins) {
if (sc.checkCollision(r)) {
progress++;
superCoins.remove(sc);
}
}
}
break;
}
return true;
}
And the SuperCoin:
public class SuperCoin {
private Bitmap bm;
public int x, y, orgY;
Clicker cl;
private Long startTime;
Random r = new Random();
public SuperCoin(Bitmap bm, int x, int y, Clicker c){
this.x = x;
this.y = y;
this.orgY = y;
this.bm = bm;
this.cl = c;
startTime = System.nanoTime();
bounds = new Rect(x, y, x + bm.getWidth(), y + bm.getHeight());
}
private Rect bounds;
public boolean checkCollision(Rect second){
if(second.intersect(bounds)){
return true;
}
return false;
}
private int velX = 0, velY = 0;
public void render(Canvas c){
long elapsed = (System.nanoTime()-startTime)/1000000;
if(elapsed>50) {
int cx;
cx = r.nextInt(2);
if(cx == 0){
velX = r.nextInt(4);
}else if(cx == 1){
velX = -r.nextInt(4);
}
velY = r.nextInt(10) + 1;
startTime = System.nanoTime();
}
if(x < 0) velX = +2;
if(x > Clicker.WIDTH) velX = -2;
x += velX;
y -= velY;
c.drawBitmap(bm, x, y, null);
}
}
How can I check collision between the two different when the MotionEvent X coordinate is bigger than the screen's scaled max coordinates?
Honestly, I am not completly sure why the Rect defined in the SuperCoin class is different from the one defined in the onTouchEvent method. I'm guessing because the X and Y is permanently different between the one defined by MotionEvent and the ones defined by the scaled canvas. The Rect in the SuperCoin class goes by the width of the Bitmap it has been passed. It scales it with the width and height of the Bitmap.
After looking through StackOverflow and Google for the past 2 days looking for something that comes close to a solution, I came over this: Get Canvas coordinates after scaling up/down or dragging in android Which solved the problem. It was really hard to find because the title was slightly misleading(of the other question)
float px = e.getX() / mScaleFactorX;
float py = e.getY() / mScaleFactorY;
int ipy = (int) py;
int ipx = (int) px;
Rect r = new Rect(ipx, ipy, ipx+2, ipy+2);
I added this as an answer and accepting it so it no longer will be an unanswered question as it is solved. The code above converts the coordinates to integers so they can be used for checking collision between the finger and the object I'm checking with
Don't scale the canvas directly. Make a Matrix object, scale that once. Then concat that to the canvas. Then you can make an inverted matrix for your touch events.
And just make the invert matrix whenever you change the view matrix:
viewMatrix = new Matrix();
viewMatrix.scale(scalefactor,scalefactor);
invertMatrix = new Matrix(viewMatrix);
invertMatrix.invert(invertMatrix);
Then apply these two matrices to the relevant events.
#Override
public boolean onTouchEvent(MotionEvent event) {
event.transform(invertMatrix);
And then on the draw events, concat the matrix.
#Override
protected void onDraw(Canvas canvas) {
canvas.concat(viewMatrix);
And you're done. everything is taken care of for you. Whatever modifications you do to the view matrix will change your viewbox and your touch events will be translated into that same scene too.
If you want to add panning or rotation, or even skew the view, it's all taken care of. Just apply that to the matrix, get the inverted matrix, and the view will look that way and the touch events will respond as you expect.
I am doing a Pong clone and I've implemented the ball as a Rect() object. Now to move the ball I use the Rect.offset(dx,dy), which offsets the ball by a specefied speed. For bouncing the ball off a wall I multiply the velocity of respective axis by -1. Now for bounce along the Y-axis , it bounces perfectly, but along the X-axis it starts behavioral weirdly. Is this some glitch with the android studio or am I doing something wrong?
package com.nblsoft.pong;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
public class PongLogic extends View {
//set screen constrains in dip
Configuration configuration = this.getResources().getConfiguration();
int dpHeight = configuration.screenHeightDp; //The current height of the available screen space, in dp units, corresponding to screen height resource qualifier.
int dpWidth = configuration.screenWidthDp; //The current width of the available screen space, in dp units, corresponding to screen width resource qualifier.
//int smallestScreenWidthDp = configuration.smallestScreenWidthDp; //The smallest screen size an application will see in normal operation, corresponding to smallest screen width resource qualifier.
//DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
//float dpHeight = displayMetrics.heightPixels / displayMetrics.density;
//float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
private int dptopixel(int DESIRED_DP_VALUE){
final float scale = getResources().getDisplayMetrics().density;
return (int)((DESIRED_DP_VALUE) * scale + 0.5f);
}
private int pixeltodp(int DESIRED_PIXEL_VALUE){
final float scale = getResources().getDisplayMetrics().density;
return (int) ((DESIRED_PIXEL_VALUE) - 0.5f / scale);
}
//set paddle size, speed, position vector
int AI_paddle_pos_x = 4 * (dptopixel(dpWidth)/100); //3 for 320x480, 10 for 1080x1920 etc.
int paddle_width = (dptopixel(dpWidth)/10); //
int AI_paddle_pos_y = (dptopixel(dpHeight)/10); //48 for 320x480, 190 for 1080x1920 etc.
int paddle_height = (dptopixel(dpHeight)/100) + 3; //the paddle is 100% of the total height of phone.
int user_paddle_pos_x = 4 * (dptopixel(dpWidth)/100) ;
int user_paddle_pos_y = dptopixel(dpHeight) - ((dptopixel(dpHeight)/10) + (dptopixel(dpHeight)/100) + 3) ;
//User Paddle
public Rect paddle_user = new Rect(user_paddle_pos_x,
user_paddle_pos_y,
user_paddle_pos_x + paddle_width,
user_paddle_pos_y + paddle_height);
//AI paddle
Rect paddle_AI = new Rect(AI_paddle_pos_x,
AI_paddle_pos_y,
AI_paddle_pos_x + paddle_width,
AI_paddle_pos_y + paddle_height);
//set ball position vector, Velocity vector, acceleration
int ball_pos_x = 0 ;
int ball_pos_y = (dptopixel(dpHeight)/2) ;
int ball_size = dptopixel(dpWidth)/100 ;
int ball_velocity = 3;
// Ball
Rect ball = new Rect(ball_pos_x,
ball_pos_y,
ball_pos_x+ball_size,
ball_pos_y+ball_size);
//Override onDraw method
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
Paint mytext = new Paint();
mytext.setColor(Color.WHITE);
//mytext.setStyle(Paint.Style.STROKE);
//mytext.setStrokeWidth(2);
// Draw Middle point
canvas.drawRect(0, ((dptopixel(dpHeight)) / 2), (dptopixel(dpWidth)), (((dptopixel(dpHeight)) / 2) + 2), mytext);
//draw both paddles
canvas.drawRect(paddle_user,mytext);
canvas.drawRect(paddle_AI, mytext);
//draw ball
canvas.drawRect(ball,mytext);
//Practise Methods
//canvas.drawText(Integer.toString(dptopixel(dpHeight)),300,300,mytext);
//canvas.drawText(Integer.toString(dptopixel(dpWidth)), 400, 400, mytext);
//canvas.drawText(Integer.toString(dpHeight),500,500,mytext);
//canvas.drawText(Integer.toString(dpWidth),600,600,mytext);
//canvas.drawText("Fuck", 700, 700, mytext);
//canvas.drawRect(0,0,dptopixel(dpWidth),dptopixel(dpHeight),mytext);
//Game Loop Updater
update();
invalidate();
}
private void update() {
if (ball.centerX() > (dptopixel(dpWidth))/2){
ball.offset(-ball_velocity,ball_velocity);
}
else{
ball.offset(ball_velocity,ball_velocity);
}
}
//Override Touch method
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
paddle_user.offset(10,0);
}
return true;
}
/* #Override
public boolean onTouch(View v, MotionEvent event) {
this.paddle_user.offsetTo(10,10);
return true; //Event Handled
}
*/
public PongLogic(Context context) {
super(context);
setBackgroundColor(Color.BLACK); //to set background
this.setFocusableInTouchMode(true); //to enable touch mode
}
}
Ok I've Figured out why the offset method was behaving erratically. First I had the original offset position in the else part of the conditional. Second I was negating the dx component only when the if evaluated to true. Which happened for just one frame because I was evaluating Rect object's X position. Which cause it to become false as soon as the -dx was subtracted from the ball's position.
To resolve it I had to place the offset out of the conditional and pass two separate values for the dx and dy. Then I tweaked the conditional so that it only assigned positive and negative values to the variables which were later passed into offset() function, Rather then calling the offset function itself.
This is the new update() method
private void update() {
if (ball.centerX() > (dptopixel(dpWidth))/2){
ball_velocity_x = -3;
}
else if (ball.centerY() > (dptopixel(dpHeight))) {
ball_velocity_y = -3;
}
ball.offset(ball_velocity_x, ball_velocity_y);
}
I just need to set a high res image, but wants to know if its possible to set it in xml. I want to be able to set it even if it will be bigger than the mobile screen.
EDIT: I want to add a huge image, so the user will have to scroll it. Something like a map, but need to add clickable areas on it and I only know how to do this in xml. That's why I asked to add the image using xml not in java. But if someone can tell me how to add clickable areas in java. That will do it better, since I already have the image but using java and its able to scroll.
EDIT: Heres where I got the code from : Anddev.org I'm using exactly the same code, just with other image.
What are you trying for? In android the resolutions are based on like below -
Screen Support
MultipleResolution
Just read out this. Hope these two links enough for your query.
Have a look at this sample code to see how to scroll an image larger than the screen. It also does bitmap caching to speed up drawing of a complex image, and shows how to respond to long-taps and double-taps at any point on the image.
Ok I'm posting this as an answer, just because it will be easier to post this. (It's not working how I want it to, but I got something) I want to share this so anyone else, who knows how to, can help me out here to find a solution for this one. This is the code
package com.example.largeimagescroller;
import android.app.Activity;
import android.os.Bundle;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;
public class LargeImageScroller extends Activity {
// Physical display width and height.
private static int displayWidth = 0;
private static int displayHeight = 0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// displayWidth and displayHeight will change depending on screen
// orientation. To get these dynamically, we should hook
// onSizeChanged().
// This simple example uses only landscape mode, so it's ok to get them
// once on startup and use those values throughout.
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();
// SampleView constructor must be constructed last as it needs the
// displayWidth and displayHeight we just got.
setContentView(new SampleView(this));
}
private static class SampleView extends View {
private static Bitmap bmLargeImage; // bitmap large enough to be
// scrolled
private static Rect displayRect = null; // rect we display to
private Rect scrollRect = null; // rect we scroll over our bitmap with
private int scrollRectX = 0; // current left location of scroll rect
private int scrollRectY = 0; // current top location of scroll rect
private float scrollByX = 0; // x amount to scroll by
private float scrollByY = 0; // y amount to scroll by
private float startX = 0; // track x from one ACTION_MOVE to the next
private float startY = 0; // track y from one ACTION_MOVE to the next
public SampleView(Context context) {
super(context);
// Destination rect for our main canvas draw. It never changes.
displayRect = new Rect(0, 0, displayWidth, displayHeight);
// Scroll rect: this will be used to 'scroll around' over the
// bitmap in memory. Initialize as above.
scrollRect = new Rect(0, 0, displayWidth, displayHeight);
// Load a large bitmap into an offscreen area of memory.
bmLargeImage = BitmapFactory.decodeResource(getResources(),
R.drawable.alienware);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
//This code define what to do if you touch the x and y coordinates.
float x1 = event.getX();
float y1 = event.getY();
switch (event.getActionMasked()){
case MotionEvent.ACTION_DOWN:
if (x1>150 & x1<200 & y1>400 & y1<500){
Toast.makeText(getContext(), "Touched the coordinates.", Toast.LENGTH_SHORT).show();
}
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Remember our initial down event location.
startX = event.getRawX();
startY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float x = event.getRawX();
float y = event.getRawY();
// Calculate move update. This will happen many times
// during the course of a single movement gesture.
scrollByX = x - startX; // move update x increment
scrollByY = y - startY; // move update y increment
startX = x; // reset initial values to latest
startY = y;
invalidate(); // force a redraw
break;
}
return true; // done with this event so consume it
}
#Override
protected void onDraw(Canvas canvas) {
// Our move updates are calculated in ACTION_MOVE in the opposite
// direction
// from how we want to move the scroll rect. Think of this as
// dragging to
// the left being the same as sliding the scroll rect to the right.
int newScrollRectX = scrollRectX - (int) scrollByX;
int newScrollRectY = scrollRectY - (int) scrollByY;
// Don't scroll off the left or right edges of the bitmap.
if (newScrollRectX < 0)
newScrollRectX = 0;
else if (newScrollRectX > (bmLargeImage.getWidth() - displayWidth))
newScrollRectX = (bmLargeImage.getWidth() - displayWidth);
// Don't scroll off the top or bottom edges of the bitmap.
if (newScrollRectY < 0)
newScrollRectY = 0;
else if (newScrollRectY > (bmLargeImage.getHeight() - displayHeight))
newScrollRectY = (bmLargeImage.getHeight() - displayHeight);
// We have our updated scroll rect coordinates, set them and draw.
scrollRect.set(newScrollRectX, newScrollRectY, newScrollRectX
+ displayWidth, newScrollRectY + displayHeight);
Paint paint = new Paint();
canvas.drawBitmap(bmLargeImage, scrollRect, displayRect, paint);
// Reset current scroll coordinates to reflect the latest updates,
// so we can repeat this update process.
scrollRectX = newScrollRectX;
scrollRectY = newScrollRectY;
}
}
}
I rec the application in use. (Since touch events don't work with cpu emulators I did it on my phone)
http://www.youtube.com/watch?v=NrszZoDenXE&feature=youtube_gdata
As you can see its taking the touch, but it moves with the scroll rect, so the question will be: How can I do the touch to stay in the image, so it doesn't move with the scrolling rect?
Thanks
I have seen how to draw a shape in Android, but what I want to know is how to rescale the shape when the user touches over the shape.
Imagine a square into a screen corner, so when you touch it, it grows until fitting the whole screen. I'd like to make that with a transition, animated, not instant.
Any idea of how to do that, or any known resource?
Android has built-in support for Animations. You can find many examples by searching the Web. This one is a good start.
In order to make your shapes touchable, you can implement them by overriding the View class (a nice example can be found here). Then you can use View.OnTouchListener.
The built in Animations are nice in Android but they aren't the most efficient by any means. When performance is a must I would recommend creating your own method. What I would do is create a class that extends View and give it a bounding box (Rect/RectF) and a circle. Then you can use the bounding box to detect when the circle is touched.
public class Circle extends View {
public static final float SCALE_AMOUNT = 1.0f;
public RectF boundingBox;
private Paint paint;
private float circleCenterX, circleCenterY, circleRadius;
private float x, y;
public Circle(Context context) {
super(context);
// Create paint
paint = new Paint();
paint.setColor(Color.BLACK);
paint.setAntiAlias(true);
// Set circle start radius
circleRadius = 50.0f;
// Set start x and y (this is the upper left hand corner)
x = 100.0f;
y = 100.0f;
// Create boundingBox
boundingBox = new RectF();
boundingBox.left = x;
boundingBox.top = y;
boundingBox.right = x + (circleRadius*2);
boundingBox.bottom = y + (circleRadius*2);
// Set circleCenterX and circleCenterY (the center of the bounding box and circle)
circleCenterX = x + circleRadius;
circleCenterY = y + circleRadius;
}
public void scale(boolean scaleUp) {
float scaleBy = (scaleUp) ? SCALE_AMOUNT : -SCALE_AMOUNT;
// Update circleRadius
circleRadius += scaleBy;
// Update the bounding box
boundingBox.left = x;
boundingBox.top = y;
boundingBox.right = x + (circleRadius*2);
boundingBox.bottom = y + (circleRadius*2);
// Update the circle center positions
circleCenterX = x + circleRadius;
circleCenterY = y + circleRadius;
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawCircle(circleCenterX, circleCenterY, circleRadius, paint);
}
}
... Then in your Activity class override the onTouchEvent() method and check if your Circle is touched.
Circle circle = new Circle(this);
#Override
public void onDraw(Canvas canvas) {
circle.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
float x = event.getX();
float y = event.getY();
// Detect if pointer goes down on screen
if(action == MotionEvent.ACTION_DOWN) {
if(circle.boundingBox.contains(x, y) == true) {
// Circle was touched so scale it
circle.scale(true); // true is scale up, false is scale down
}
}
return true;
}
... This will scale your circle/rectangle every time you touch it. If you wanted to make it continually grow you could have a boolean variable that gets set to true when you touch the shape and grows until you pick your finger up. I haven't tried this code, just typed it up real quick so it may not compile but this is going to be you're best bet. It is really easy to add many shapes and detect touches on all of the shapes. Add different effects to each one... etc. I didn't want to do all of it for you but this should point you in the right direction.
Maybe this github project could help you: https://github.com/markushi/android-circlebutton