How do you fix the position of an image drawn onto a canvas with a large scrollable background?
picture this:
the background image is that of a room, the object images are bed, door etc.
the object images are drawn on top of the background.
when i scroll the background, the objects should move with respect to the background image, correct?
the problem is the object images also move but the position doesn't stay the same, i.e they shift from their original positions.
here is my full class implementation.
Bitmap bmImage;
SpriteAnim8 anim;
MThread thread;
PersonAnimated person, person2;
Canvas canvas = new Canvas();
Rect displayRect = null;
Rect scrollRect = null;
int scrollRectX = 0, scrollRectY = 0;
float scrollByX = 0, scrollByY = 0;
float startX = 0, startY = 0;
int initX = 200, initY = 200;
float a = initX, b = initY;
public MGamePanel(Context context) {
super(context);
// adding the callback (this) to the surface holder to intercept events
getHolder().addCallback(this);
// create Person and load bitmap
person = new PersonAnimated(BitmapFactory.decodeResource(getResources(), R.drawable.dad_anim),
10, 200 /* initial position */,
45, 56 /* width and height of sprite */,
5, 10); /* FPS and number of frames in the animation */
// Destination rect for our main canvas draw
displayRect = new Rect(0, 0, SpriteAnim8.displayWidth, SpriteAnim8.displayHeight);
// Scroll rect: this will be used to 'scroll around' over the bitmap
scrollRect = new Rect(0, 0, SpriteAnim8.displayWidth, SpriteAnim8.displayHeight);
// Load a large bitmap
bmImage = BitmapFactory.decodeResource(getResources(), R.drawable.l1_plain);
// create the game loop thread
thread = new MThread(getHolder(), this);
// make the GamePanel focusable so it can handle events
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// at this point the surface is created and we can safely start the game
// loop
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Surface is being destroyed");
// tell the thread to shut down and wait for it to finish
// this is a clean shutdown
boolean retry = true;
while (retry) {
try {
thread.setRunning(false);
((Activity) getContext()).finish();
retry = false;
} catch (Exception e) {
// try again shutting down the thread
}
}
Log.d(TAG, "Thread was shut down cleanly");
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// delegating event handling to the person
startX = event.getRawX();
startY = event.getRawY();
// }
case MotionEvent.ACTION_MOVE:
float x = event.getRawX();
float y = event.getRawY();
// Calculate move update.
mScrollByX = x - mStartX + mOldScrollByX; // move update x increment
mScrollByY = y - mStartY + mOldScrollByX; // move update y increment
onDraw(canvas);
break;
case MotionEvent.ACTION_UP:
mOldScrollByX = mScrollByX;
mOldScrollByY = mScrollByY;
break;
}
return true;
}
public void render(Canvas canvas) {
Paint paint = new Paint();
canvas.drawBitmap(bmImage, scrollRect, displayRect, paint);
person.draw(canvas);
}
#Override
protected void onDraw(Canvas canvas) {
int newScrollRectX = scrollRectX - (int) scrollByX;
int newScrollRectY = scrollRectY - (int) scrollByY;
// Prevent scrolling off the left or right edges of the bitmap.
if (newScrollRectX < 0) {
newScrollRectX = 0;
} else if (newScrollRectX > (bmImage.getWidth() - SpriteAnim8.displayWidth)) {
newScrollRectX = (bmImage.getWidth() - SpriteAnim8.displayWidth);
}
// Prevent scrolling off the top or bottom edges of the bitmap.
if (newScrollRectY < 0) {
newScrollRectY = 0;
} else if (newScrollRectY > (bmImage.getHeight() - SpriteAnim8.displayHeight)) {
newScrollRectY = (bmImage.getHeight() - SpriteAnim8.displayHeight);
}
// set the updated scroll rect coordinates.
scrollRect.set(newScrollRectX, newScrollRectY, newScrollRectX
+ SpriteAnim8.displayWidth, newScrollRectY
+ SpriteAnim8.displayHeight);
// Reset current scroll coordinates to reflect the latest updates so we
// can repeat
scrollRectX = newScrollRectX;
scrollRectY = newScrollRectY;
person.setX(person.getX() + scrollByX);
person.setY(person.getY() + scrollByY);
}
is this correct?
Here is a code how to scroll a picture/image which is drawn on canvas:
//define points on the custome view class level
PointF touchStart = new PointF();
PointF picStart = new PointF();
PointF prevPicStart = new PointF();
Handle the touch and remember the previous poistion of the picture
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
touchStart.set(ev.getX(), ev.getY());
break;
}
case MotionEvent.ACTION_MOVE: {
float newX = ev.getX() - touchStart.x + prevPicStart.x;
float newY = ev.getY() - touchStart.y + prevPicStart.y;
//assuming the the picture is bigger than the screen
if ((newX <= 0 && newX > 0 - pic.getWidth() + screenW)){
picStart.x = newX;
}
if ((newY <= 0 && newY > 0 - pic.getHeight() + screenH)){
picStart.y = newY;
}
invalidate();
break;
}
case MotionEvent.ACTION_UP:
prevPicStart.x = picStart.x;
prevPicStart.y = picStart.y;
break;
}
return true;
}
In onDraw
canvas.drawBitmap(pic, picStart.x, picStart.y, null);
Not sure about your code, but you can use a FrameLayout with background that you want for room and add ScrollView as its child, which will contain the other objects which are scrollable.
HTH !
Related
I want to control my simple android game (arcanoid) with accelerometer. Paddle in game is now being controlled with touch screen event. I want to control it with accelerometer.
I tried to implement accelerometer into GameActivity class which is "controlling" BreakOutView class like this:
public class GameActivity extends Activity implements SensorEventListener {
// gameView will be the view of the Menu_Layout
// It will also hold the logic of the Menu_Layout
// and respond to screen touches as well
BreakOutView breakoutView;
private SensorManager sManager;
Sensor accelerometer;
Paddle paddle;
float x;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
breakoutView = new BreakOutView(this);
sManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); // zisk managera
if (sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null) {
accelerometer = sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
// Initialize gameView and set it as the view
setContentView(breakoutView);
}
// This method executes when the player starts the Game
#Override
protected void onResume() {
super.onResume();
sManager.registerListener(this,accelerometer,SensorManager.SENSOR_DELAY_NORMAL);
// Tell the gameView resume method to execute
breakoutView.resume();
}
// This method executes when the player quits the Menu_Layout
#Override
protected void onPause() {
super.onPause();
sManager.unregisterListener(this);
// Tell the gameView pause method to execute
breakoutView.pause();
}
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
{
x = sensorEvent.values[0];
if (x > 0) {
breakoutView.paddle.setMovementState(paddle.LEFT);
}
else { breakoutView.paddle.setMovementState(paddle.RIGHT);
}
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
}
This is code for BreakOutView class.
onTouchEvent method is now disabled because I want to use accelerometer to control Paddle in application.
public class BreakOutView extends SurfaceView implements Runnable{
// This is our thread
Thread gameThread = null;
// This is new. We need a SurfaceHolder
// When we use Paint and Canvas in a thread
// We will see it in action in the draw method soon.
SurfaceHolder ourHolder;
// A boolean which we will set and unset
// when the Menu_Layout is running- or not.
volatile boolean playing;
// Game is paused at the start
boolean paused = true;
// A Canvas and a Paint object
Canvas canvas;
Paint paint;
// This variable tracks the Menu_Layout frame rate
long fps;
Bitmap bitmapBob;
Bitmap bitmapBall;
Bitmap bitmapPaddal;
Bitmap bitmapBrick1;
Bitmap bitmapBrick2;
Bitmap bitmapBrick3;
// The size of the screen in pixels
int screenX;
int screenY;
// The players paddle
Paddle paddle;
// A ball
Ball ball;
// Up to 200 bricks
Brick[] bricks = new Brick[24];
int numBricks = 0;
// For sound FX
SoundPool soundPool;
int beep1ID = -1;
int beep2ID = -1;
int beep3ID = -1;
int loseLifeID = -1;
int explodeID = -1;
// The score
int score = 0;
int level = 1;
// Lives
int lives = 3;
Rect dest;
DisplayMetrics dm;
int densityDpi;
// When we initialize (call new()) on BreakOutView
// This special constructor method runs
public BreakOutView(Context context) {
super(context);
// The next line of code asks the
// SurfaceView class to set up our object.
// How kind.
// Initialize ourHolder and paint objects
ourHolder = getHolder();
paint = new Paint();
// Get a Display object to access screen details
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
// Load the resolution into a Point object
Point size = new Point();
// TODO target API < 13
display.getSize(size);
screenX = size.x;
screenY = size.y;
// using dpi to set sizes for objects
dm = context.getResources().getDisplayMetrics();
densityDpi = dm.densityDpi;
paddle = new Paddle(screenX, screenY, densityDpi);
// Create a ball
ball = new Ball(screenX, screenY);
// Load the sounds
// This SoundPool is deprecated but don't worry
soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
try {
// Create objects of the 2 required classes
AssetManager assetManager = context.getAssets();
AssetFileDescriptor descriptor;
// Load our fx in memory ready for use
descriptor = assetManager.openFd("beep1.wav");
beep1ID = soundPool.load(descriptor, 0);
descriptor = assetManager.openFd("beep2.wav");
beep2ID = soundPool.load(descriptor, 0);
descriptor = assetManager.openFd("beep3.wav");
beep3ID = soundPool.load(descriptor, 0);
descriptor = assetManager.openFd("loseLife.wav");
loseLifeID = soundPool.load(descriptor, 0);
descriptor = assetManager.openFd("explode.wav");
explodeID = soundPool.load(descriptor, 0);
} catch (IOException e) {
// Print an error message to the console
Log.e("error", "failed to load sound files");
}
// Load Images from resource files
bitmapBob = BitmapFactory.decodeResource(this.getResources(), R.drawable.wall);
bitmapBall = BitmapFactory.decodeResource(this.getResources(), R.drawable.ball);
bitmapPaddal = BitmapFactory.decodeResource(this.getResources(), R.drawable.ball);
bitmapBrick1 = BitmapFactory.decodeResource(this.getResources(), R.drawable.brick_red);
bitmapBrick2 = BitmapFactory.decodeResource(this.getResources(), R.drawable.brick_green);
bitmapBrick3 = BitmapFactory.decodeResource(this.getResources(), R.drawable.brick_monster);
//Make Sizes Depending on DPI
int heightX = densityDpi / 8;
float length_Paddal = densityDpi / 1.50f;
int height_Paddal = densityDpi / 7;
int brickWidth = screenX / 8;
int brickHeight = screenY / 10;
bitmapBall = getResizedBitmap(bitmapBall, heightX, heightX);
bitmapPaddal = getResizedBitmap(bitmapPaddal, length_Paddal, height_Paddal);
bitmapBrick1 = getResizedBitmap(bitmapBrick1, brickWidth, brickHeight);
bitmapBrick2 = getResizedBitmap(bitmapBrick2, brickWidth, brickHeight);
bitmapBrick3 = getResizedBitmap(bitmapBrick3, brickWidth, brickHeight);
// Create bricks for level 1
createBricksAndRestart(1);
}
public void createBricksAndRestart(int Xlevel) {
// Put the ball back to the start
ball.reset(screenX, screenY);
level = Xlevel;
switch (Xlevel) {
case 2:
// level 2
ball.xVelocity = 600;
ball.yVelocity = -1000;
break;
// level 3
case 3:
ball.xVelocity = 1000;
ball.yVelocity = -1400;
break;
// level 1
default:
ball.xVelocity = 400;
ball.yVelocity = -800;
break;
}
// Brick Size
int brickWidth = screenX / 8;
int brickHeight = screenY / 10;
// Build a wall of bricks
numBricks = 0;
for (int column = 0; column < 8; column++) {
for (int row = 0; row < 3; row++) {
bricks[numBricks] = new Brick(row, column, brickWidth, brickHeight);
numBricks++;
}
}
// if Game is over reset scores ,lives &Level
if (lives == 0) {
score = 0;
lives = 3;
level = 1;
}
}
#Override
public void run() {
while (playing) {
// Capture the current time in milliseconds in startFrameTime
long startFrameTime = System.currentTimeMillis();
// Update the frame
if (!paused) {
update();
}
// Draw the frame
draw();
// Calculate the fps this frame
// We can then use the result to
// time animations and more.
long timeThisFrame = System.currentTimeMillis() - startFrameTime;
if (timeThisFrame >= 1) {
fps = 1000 / timeThisFrame;
}
}
}
// Everything that needs to be updated goes in here
// Movement, collision detection etc.
public void update() {
// Move the paddle if required
paddle.update(fps);
ball.update(fps);
// Check for ball colliding with a brick
for (int i = 0; i < numBricks; i++) {
if (bricks[i].getVisibility()) {
if (RectF.intersects(bricks[i].getRect(), ball.getRect())) {
bricks[i].setInvisible();
ball.reverseYVelocity();
score = score + 10;
soundPool.play(explodeID, 1, 1, 0, 0, 1);
}
}
}
// Check for ball colliding with paddle
if (
ball.getRect().intersect(paddle.getRect()) ||
RectF.intersects(paddle.getRect(), ball.getRect()) ||
paddle.getRect().intersect(ball.getRect())
) {
ball.reverseYVelocity();
// ReverseX Direction + IncreaseX speed
if (paddle.getMovementState() == paddle.RIGHT && ball.xVelocity < 0 || paddle.getMovementState() == paddle.LEFT && ball.xVelocity > 0) {
ball.reverseXVelocity();
}
// SameX Direction + IncreaseX speed
else if (paddle.getMovementState() == paddle.RIGHT && ball.xVelocity > 0 || paddle.getMovementState() == paddle.LEFT && ball.xVelocity < 0) {
ball.sameXVelocity();
}
/*// Paddle is still, DecreaseX speed
else if (paddle.getMovementState() == paddle.STOPPED) {
ball.zeroXVelocity();
}*/
// Some intersection Bugs
ball.clearObstacleY(paddle.getRect().top - 20);
soundPool.play(beep1ID, 1, 1, 0, 0, 1);
}
// Bounce the ball back when it hits the bottom of screen
// And Lose a life
if (ball.getRect().bottom > screenY) {
ball.reverseYVelocity();
ball.clearObstacleY(screenY - 5);
// Lose a life
lives--;
soundPool.play(loseLifeID, 1, 1, 0, 0, 1);
if (lives == 0) {
paused = true;
//draw Loss;
canvas = ourHolder.lockCanvas();
paint.setColor(getResources().getColor(R.color.orange));
paint.setTextSize(getResources().getDimension(R.dimen.text_size_big));
canvas.drawText("أنت خسرت!",
screenX / 2 - (densityDpi / 1.90f), screenY / 2 + (densityDpi), paint);
ourHolder.unlockCanvasAndPost(canvas);
try {
// Wait 3 seconds then reset a new game
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Create bricks at level 1
createBricksAndRestart(1);
}
}
// Pause if cleared screen
if (score == numBricks * 10) {
// Create bricks at level 2
createBricksAndRestart(2);
// fix for a pause bug
// so that it won't Pause After finishing the Game
score = score + 10;
// Gift the player with 1 new live
lives = lives + 1;
} else if (score == (numBricks * 20) + 10) {
// Create bricks at level 3
createBricksAndRestart(3);
// fix for a pause bug
// so that it won't Pause After finishing the Game
score = score + 10;
// Gift the player with 2 new lives
lives = lives + 2;
}
// Pause if cleared screen
// if score equals to the whole Bricks scores after 3 levels
else if (score == (numBricks * 10 * 3) + 20) {
paused = true;
}
// Bounce the ball back when it hits the top of screen
if (ball.getRect().top < 0) {
ball.reverseYVelocity();
ball.clearObstacleY(40);
soundPool.play(beep2ID, 1, 1, 0, 0, 1);
}
// If the ball hits left wall bounce
if (ball.getRect().left < 0) {
ball.reverseXVelocity();
ball.clearObstacleX(2);
soundPool.play(beep3ID, 1, 1, 0, 0, 1);
}
// If the ball hits right wall Velocity
if (ball.getRect().right > screenX) {
ball.reverseXVelocity();
ball.clearObstacleX(screenX - 57);
soundPool.play(beep3ID, 1, 1, 0, 0, 1);
}
}
// Draw the newly updated scene
public void draw() {
// Make sure our drawing surface is valid or we crash
if (ourHolder.getSurface().isValid()) {
// Lock the canvas ready to draw
canvas = ourHolder.lockCanvas();
// Draw the background color
// canvas.drawColor(getResources().getColor(R.color.deeppurple));
dest = new Rect(0, 0, getWidth(), getHeight());
// Draw bob as background with dest size
canvas.drawBitmap(bitmapBob, null, dest, paint);
// Choose the brush color for drawing
paint.setColor(Color.argb(255, 255, 255, 255));
// Draw the ball
// canvas.drawCircle(ball.getRect().centerX(), ball.getRect().centerY(), 25, paint);
canvas.drawBitmap(bitmapBall, ball.getRect().left, ball.getRect().top, null);
// Draw the paddle
//canvas.drawRect(paddle.getRect(), paint);
canvas.drawBitmap(bitmapPaddal, paddle.getRect().left, paddle.getRect().top, null);
// Change the brush color for drawing
// paint.setColor(getResources().getColor(R.color.redorange));
// Draw the bricks if visible
for (int i = 0; i < numBricks; i++) {
if (bricks[i].getVisibility()) {
// canvas.drawRect(bricks[i].getRect(), paint);
switch (level) {
case 1:
canvas.drawBitmap(bitmapBrick1, bricks[i].getRect().left, bricks[i].getRect().top, null);
break;
case 2:
canvas.drawBitmap(bitmapBrick2, bricks[i].getRect().left, bricks[i].getRect().top, null);
break;
case 3:
canvas.drawBitmap(bitmapBrick3, bricks[i].getRect().left, bricks[i].getRect().top, null);
break;
}
}
}
// Choose the brush color for drawing
paint.setColor(Color.argb(255, 255, 255, 255));
// Draw the score
paint.setTextSize(getResources().getDimension(R.dimen.text_size));
// Score Text
canvas.drawText(
"النقاط: " + score
, screenX - (densityDpi / 1.50f), screenY / 2, paint);
// Lives Text
canvas.drawText("الصحة: " + lives
, densityDpi / 5, screenY / 2, paint);
// Levels Text
canvas.drawText("المرحلة: " + level
, screenX / 2 - (densityDpi / 5), screenY / 2 + (densityDpi / 5), paint);
// Has the player cleared the screen?
if (score >= (numBricks * 10 * 3) + 20) {
paint.setColor(getResources().getColor(R.color.colorAccent));
paint.setTextSize(getResources().getDimension(R.dimen.text_size_big));
canvas.drawText("أنت كسبت!", screenX / 2 - (densityDpi / 1.90f), screenY / 2 + (densityDpi / 1), paint);
}
// Draw everything to the screen
ourHolder.unlockCanvasAndPost(canvas);
}
}
// If GameActivity is paused/stopped
// shutdown our thread.
public void pause() {
playing = false;
try {
gameThread.join();
} catch (InterruptedException e) {
Log.e("Error:", "joining thread");
}
}
// If GameActivity is started
// start our thread.
public void resume() {
playing = true;
gameThread = new Thread(this);
gameThread.start();
}
// The SurfaceView class implements onTouchListener
// So we can override this method and detect screen touches.
/* #Override
public boolean onTouchEvent(MotionEvent motionEvent) {
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
// Player has touched the screen
case MotionEvent.ACTION_DOWN:
if (!(lives == 0)) {
paused = false;
}
// If touch motion > Half of the Screen
if (motionEvent.getX() > screenX / 2) {
// move paddle right
paddle.setMovementState(paddle.RIGHT);
} else {
// move paddle left
paddle.setMovementState(paddle.LEFT);
}
break;
// Player has removed finger from screen
case MotionEvent.ACTION_UP:
// paddle stopped
paddle.setMovementState(paddle.STOPPED);
break;
}
return true;
}*/
// Resize Bitmap function to Handle all the Images from resources the right size
public Bitmap getResizedBitmap(Bitmap bm, float newWidth, int newHeight) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = newWidth / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// "RECREATE" THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(
bm, 0, 0, width, height, matrix, false);
bm.recycle();
return resizedBitmap;
}
}
And this is code for Paddle class:
import android.graphics.RectF;
public class Paddle {
// Which ways can the paddle move
public final int STOPPED = 0;
public final int LEFT = 1;
public final int RIGHT = 2;
int scrX;
// RectF is an object that holds four coordinates - just what we need
private RectF rect;
// How long and high our paddle will be
private float length;
private float height;
// X is the far left of the rectangle which forms our paddle
private float x;
// Y is the top coordinate
private float y;
// This will hold the pixels per second speedthat the paddle will move
private float paddleSpeed;
// Is the paddle moving and in which direction
private int paddleMoving = STOPPED;
private int MYscreenDPI;
// This the the constructor method
// When we create an object from this class we will pass
// in the screen width and height
public Paddle(int screenX, int screenY, int screenDPI) {
// Dynamic size based on each device DPI
length = screenDPI / 2;
height = screenDPI / 5;
MYscreenDPI = screenDPI;
scrX = screenX;
// Start paddle in roughly the sceen centre
x = screenX / 2;
y = screenY - screenDPI / 4.50f;
rect = new RectF(x, y, x + length, y + height);
// How fast is the paddle in pixels per second
paddleSpeed = 800;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
// This is a getter method to make the rectangle that
// defines our paddle available in BreakoutView class
public RectF getRect() {
return rect;
}
public int getMovementState() {
return paddleMoving;
}
// This method will be used to change/set if the paddle is going left, right or nowhere
public void setMovementState(int state) {
paddleMoving = state;
}
// This update method will be called from update in BreakoutView
// It determines if the paddle needs to move and changes the coordinates
// contained in rect if necessary
public void update(long fps) {
if (paddleMoving == LEFT) {
// to fix Paddle going off the Screen
if (x >= -MYscreenDPI / 10)
// Decrement position
x = x - paddleSpeed / fps;
}
if (paddleMoving == RIGHT) {
// to fix Paddle going off the Screen
if (x <= scrX - length - MYscreenDPI / 14)
// Increment position
x = x + paddleSpeed / fps;
}
// Apply the New position
rect.left = x;
rect.right = x + length;
}
}
When I tried to run it application is always crashing and not working. When I try to use onTouchEvent method to control paddle it is not crashing but accelerometer is not working.
I know that I'm doing something bad. I'm trying to figure it out for like one week but I don't know how to make it working.
If you can tell me any suggestions I will be really thankful. Thank you for all responses.
--EDIT--
Here is error message from logcat when I clicked on new game in emulator:
2019-04-13 17:30:55.116 7082-7082/com.kazaky.breakout E/SensorManager: Exception dispatching input event. 2019-04-13 17:30:55.122 7082-7082/com.kazaky.breakout E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.kazaky.breakout, PID: 7082
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference
at com.kazaky.breakout.GameActivity.onSensorChanged(GameActivity.java:73)
at android.hardware.SystemSensorManager$SensorEventQueue.dispatchSensorEvent(SystemSensorManager.java:833)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:326)
at android.os.Looper.loop(Looper.java:160)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 2019-04-13 17:31:06.794 7082-7117/com.kazaky.breakout E/Surface: queueBuffer: error queuing buffer to SurfaceTexture, -19 2019-04-13 17:31:06.794 7082-7117/com.kazaky.breakout E/Surface: queueBuffer (handle=0xe9f16140) failed (No such device)
As suggested by the Stack Trace you are trying to access a null object, which in this case I'm guessing sensroEvent in onSensorChanged.
Add a nullability check and return if sensorEvent is null, like so:
if (sensorEvent == null) return
and write the rest of the function after the Guard Condition.
I have an app where I draw text on bitmap using the position specified by the user. When I ask canvas to draw the text using the x,y coordinates , the text draws in wrong place.
Where I position text Where it draws after I save
My code to save the image
private void drawText(Bitmap bitmap, TextView mText) {
try {
Resources resources = getResources();
android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig();
// set default bitmap config if none
if (bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
// resource bitmaps are imutable,
// so we need to convert it to mutable one
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
paint.setTextSize(text.getTextSize());
Rect bounds = new Rect();
paint.getTextBounds(mText.getText().toString(), 0, mText.length(), bounds);
canvas.drawText(mText.getText().toString(), text.getX() , text.getY(), paint);
ImageView im = (ImageView) findViewById(R.id.imageView);
im.setImageBitmap(bitmap);
saveBitmap(bitmap);
} catch (Exception e) {
// TODO: handle exception
}
}
Code to drag textview
textView.setOnTouchListener(new View.OnTouchListener() {
int initialX = 0;
int initialY = 0;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
initialX = (int) event.getX();
initialY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int currentX = (int) event.getX();
int currentY = (int) event.getY();
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) textView.getLayoutParams();
int left = lp.leftMargin + (currentX - initialX);
int top = lp.topMargin + (currentY - initialY);
int right = lp.rightMargin - (currentX - initialX);
int bottom = lp.bottomMargin - (currentY - initialY);
lp.rightMargin = right;
lp.leftMargin = left;
lp.bottomMargin = bottom;
lp.topMargin = top;
textView.setLayoutParams(lp);
break;
default:
break;
}
return true;
}
});
findViewById(R.id.save).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
drawText(getBitmap(), textView);
}
});
My issue is that I'm drawing rectangles to a screen and want to be able to scroll in a direction and continue drawing. This is meant to be a basic house plan drawing application.
I begin by drawing a square:
I then click the MOVE SCREEN button and switch to "move mode". I pinch zoom out to draw the adjacent room:
I then want to be able to draw this:
However, as soon as I click DRAW mode and start drawing the second room, this happens:
I.e. it reverts to the original zoom and draws in the wrong place. I realize its probably my code in the onDraw() method. Here's my code:
class HomerView extends View { // the custom View for drawing on
// set up Bitmap, canvas, path and paint
private Bitmap myBitmap; // the initial image we turn into our canvas
private Canvas myCanvas; // the canvas we are drawing on
private Rect myRect; // the mathematical path of the lines we draw
private Paint myBitmapPaint; // the paint we use to draw the bitmap
// get the width of the entire tablet screen
private int screenWidth = getContext().getResources().getDisplayMetrics().widthPixels;
// get the height of the entire tablet screen
private int screenHeight = getContext().getResources().getDisplayMetrics().heightPixels;
private int mX, mY, iX, iY; // current x,y and initial x,y
private static final float TOUCH_TOLERANCE = 4;
private static final int INVALID_POINTER_ID = -1;
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
public HomerView(Context context) { // constructor of HomerView
super(context);
myBitmap = Bitmap.createBitmap(screenWidth, screenHeight,
Bitmap.Config.ARGB_8888); // set our drawable space - the bitmap
// which becomes the canvas we draw on
myCanvas = new Canvas(myBitmap); // set our canvas to our bitmap which
// we just set up
myRect = new Rect(); // make a new rect
myBitmapPaint = new Paint(Paint.DITHER_FLAG); // set dither to ON in our
// saved drawing - gives
// better color
// interaction
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
public HomerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
public HomerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
protected void onDraw(Canvas canvas) { // method used when we want to draw
// something to our canvas
super.onDraw(canvas);
if (addObjectMode == true || addApplianceMode == true) {
canvas.drawColor(Color.TRANSPARENT); // sets canvas colour
canvas.drawBitmap(myBitmap, 0, 0, myBitmapPaint); // save the canvas
// to bitmap - the
// numbers are the
// x, y coords we
// are drawing
// from
canvas.drawRect(myRect, myPaint); // draw the rectangle that the
// user has drawn using the paint
// we set up
} else if (moveMode == true) {
canvas.save();
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
canvas.drawBitmap(myBitmap, 0, 0, myBitmapPaint); // if not present
// - nothing is
// moved
canvas.restore();
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) { // if
// screen
// size
// changes,
// alter
// the
// bitmap
// size
super.onSizeChanged(w, h, oldw, oldh);
}
private void touch_Start(float x, float y) { // on finger touchdown
// check touch mode
iX = (int) (Math.round(x));
iY = (int) (Math.round(y));
mX = (int) (Math.round(x));
mY = (int) (Math.round(y));
if (addObjectMode == true) {
myRect.set(iX, iY, mX, mY);
} else if (addApplianceMode == true) {
// code to draw an appliance icon at mX, mY (with offset so icon is
// centered)
if (isLamp == true) {
Resources res = getResources();
bmp = BitmapFactory.decodeResource(res, R.drawable.lamp);
myCanvas.drawBitmap(bmp, iX - 50, iY - 50, myBitmapPaint);
} else if (isPC == true) {
Resources res = getResources();
bmp = BitmapFactory.decodeResource(res, R.drawable.pc);
myCanvas.drawBitmap(bmp, iX - 50, iY - 50, myBitmapPaint);
} else if (isKettle == true) {
Resources res = getResources();
bmp = BitmapFactory.decodeResource(res, R.drawable.kettle);
myCanvas.drawBitmap(bmp, iX - 50, iY - 50, myBitmapPaint);
} else if (isOven == true) {
Resources res = getResources();
bmp = BitmapFactory.decodeResource(res, R.drawable.oven);
myCanvas.drawBitmap(bmp, iX - 50, iY - 50, myBitmapPaint);
} else if (isTV == true) {
Resources res = getResources();
bmp = BitmapFactory.decodeResource(res, R.drawable.tv);
myCanvas.drawBitmap(bmp, iX - 50, iY - 50, myBitmapPaint);
}
}
}
private void touch_Move(float x, float y) { // on finger movement
float dX = Math.abs(x - mX); // get difference between x and my X
float dY = Math.abs(y - mY);
if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) { // if coordinates
// are outside
// screen? if
// touching hard
// enough?
mX = (int) (Math.round(x));
mY = (int) (Math.round(y));
if (addObjectMode == true) {
myRect.set(iX, iY, mX, mY);
}
}
}
#SuppressWarnings("deprecation")
private void touch_Up() { // on finger release
if (addObjectMode == true) {
myRect.set(iX, iY, mX, mY);
myCanvas.drawRect(iX, iY, mX, mY, myPaint);
if (eraseMode == false) {
dialogStarter();
}
} else if (addApplianceMode == true) {
showDialog(DIALOG_DEVICE_ENTRY);
}
}
public boolean onTouchEvent(MotionEvent event) { // on any touch event
if (addObjectMode == true || addApplianceMode == true) {
float x = event.getX(); // get current X
float y = event.getY(); // get current Y
switch (event.getAction()) { // what action is the user performing?
case MotionEvent.ACTION_DOWN: // if user is touching down
touch_Start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE: // if user is moving finger while
// touched down
touch_Move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP: // if user has released finger
touch_Up();
invalidate();
break;
}
return true;
} else if (moveMode == true) {
mScaleDetector.onTouchEvent(event);
final int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = event.getX();
final float y = event.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = event.getPointerId(0);
invalidate();
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = event
.findPointerIndex(mActivePointerId);
final float x = event.getX(pointerIndex);
final float y = event.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;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
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 = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = event.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 = event.getX(newPointerIndex);
mLastTouchY = event.getY(newPointerIndex);
mActivePointerId = event.getPointerId(newPointerIndex);
}
break;
}
}
invalidate();
return true;
} else {
return false;
}
}
public void drawApplianceIcon() {
myCanvas.drawBitmap(bmp, iX - 50, iY - 50, myBitmapPaint);
makeToast("BMP drawn to canvas = " + bmp);
}
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(0.1f, Math.min(mScaleFactor, 10.0f));
invalidate();
return true;
}
}
}
Can anyone pick apart my code so that I can draw straight on the zoomed or panned image? Am I barking up the wrong tree and should I just use a vertical and horizontal scroll bar? Zooming isn't strictly necessary.
Any help would be much appreciated! Thanks.
Your onDraw isn't scaling the canvas when in non-move mode. That means as soon as you go into any other mode, you lose the scaling factor permanently. You need to fix that.
I'm making a custom slide control. Is like a Volume wheel, so some values augment or decrease depending on rotate direction.
I have two issues with this:
The performance is really low
The garbage collector is trigger, many many times.
Well, I'm sure that i'm doing something wrong, so please give me a Light.
Am working with Android graphics on 2.1 (eclaire) SDK.
This is the code of the view that am calling from my activity:
public class DrawingView extends View {
private Paint p;
Bitmap bitmap;
Context mContext;
Canvas canvas;
private float sweepAngle;
private int _height;
private int _width;
private float lastAngle;
private int percent;
public DrawingView(Context context) {
super(context);
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
this.set_Width(display.getWidth());
this.set_Height(display.getHeight());
this.setSweepAngle(10);
mContext = context;
p = new Paint();
p.setAntiAlias(true);
}
protected int getAngleFromLocation(Point location){
int finalAngle = (int) (Math.atan2(location.y - 200, location.x - 200) * (180 / Math.PI));
return finalAngle;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
bitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(),Bitmap.Config.ARGB_8888);
this.canvas = new Canvas(bitmap);
RectF rectF = new RectF();
rectF.set(20, 20, this.get_Widtt() - this.get_Widtt()/10, this.get_Widtt() - this.get_Widtt()/10);
canvas.drawArc(rectF, 180, this.getSweepAngle(), true, p);
//invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
double increment = 3.6;
Point touchLocation = new Point();
touchLocation.x = (int)event.getX();
touchLocation.y = (int)event.getY();
canvas.drawBitmap(bitmap = Bitmap.createBitmap(canvas.getWidth(),canvas.getHeight(),Bitmap.Config.ARGB_8888),event.getX(), event.getY(),null );
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
lastAngle = this.getAngleFromLocation(touchLocation);
System.out.println("ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
System.out.println("ACTION_MOVE");
int currentAngle = this.getAngleFromLocation(touchLocation);
System.out.println("CURRENT ANGLE: " + currentAngle);
if (currentAngle > lastAngle || (currentAngle == 1 && lastAngle == 359)) {
percent += increment;
} else if (currentAngle < lastAngle) {
percent -= increment;
}
if (percent > 360) {
percent = 360;
} else if (percent < 0) {
percent = 0;
}
lastAngle = currentAngle;
this.setSweepAngle(percent);
//Write the label
//int realPercent = percent*100/360;
System.out.println("PERCENT: "+percent);
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}
No need to create a new bitmap on every frame. In fact you should try to avoid allocationg any objects at all in onDraw().
Try this:
private RectF mRectF = new RectF();
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);
mRectF.set(20, 20, this.get_Widtt() - this.get_Widtt() / 10, this.get_Widtt() - this.get_Widtt() / 10);
canvas.drawArc(rectF, 180, this.getSweepAngle(), true, p);
}
also remove the call to canvas.drawBitmap() in onTouchEvent.
It's most likely the bitmap/canvas creation that's taking so long and triggering the GC so much. You want to do as little object creation as possible during onDraw() in particular, and onTouchEvent() as well. Is there any reason you can't move that to a separate method that gets called once and reuse the same bitmap each frame? It looks like the values you're passing to createBitmap() are static, so it shouldn't be an issue.
public class DrawView extends View
{
private ColorBall[] colorballs = new ColorBall[3]; // array that holds the balls
private int balID = 0; // variable to know what ball is being dragged
/* protected Bitmap getImage(int id) {
return BitmapFactory.decodeResource(mContex.getResources(), id);
}*/
private Paint mBitmapPaint = new Paint();
public DrawView(Context context) {
super(context);
setFocusable(false); //necessary for getting the touch events
// setting the start point for the balls
Point point1 = new Point();
point1.x = 50;
point1.y = 400;
Point point2 = new Point();
point2.x = 100;
point2.y = 400;
Point point3 = new Point();
point3.x = 150;
point3.y = 400;
// declare each ball with the ColorBall class
colorballs[0] = new ColorBall(context,R.drawable.b, point1);
colorballs[2] = new ColorBall(context,R.drawable.t, point3);
}
// the method that draws the balls
#Override protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFCCCCCC);
setFocusable(false);
Log.v("Images","3333");
//if you want another background color
canvas.drawBitmap((BitmapFactory.decodeResource(getResources(),R.drawable.caralpha)), 10, -50, mBitmapPaint);
//draw the balls on the canvas
for (ColorBall ball : colorballs) {
canvas.drawBitmap(ball.getBitmap(), ball.getX(), ball.getY(), null);
}
//canvas.drawRect(10, 50, 10 + 2, 10 + 2,mBitmapPaint);
canvas.drawText("A", 10,350, mBitmapPaint);
Vector correctname=correct("B");
String name="b";
for(int i=0,xCo=20;i<correctname.size();i++)
{
try {
int image=selectImage(name.charAt(i));
canvas.drawBitmap((BitmapFactory.decodeResource(getResources(),image)), 10+xCo,350, mBitmapPaint);
xCo=xCo+100;
}
catch(Exception e)
{
}
}
}
private int selectImage(char charAt) {
switch(charAt)
{
case 'a':
return R.drawable.a;
case 'b':
return R.drawable.b;
case 't':
return R.drawable.t;
}
return 0;
}
private Vector correct(String word) {
Vector al = new Vector();
for (int i = 0; i < word.length(); i++)
{
al.add(word.charAt(i));
}
al.toString();
return al;
}
// events when touching the screen
public boolean onTouchEvent(MotionEvent event) {
int eventaction = event.getAction();
int X = (int)event.getX();
int Y = (int)event.getY();
switch (eventaction ) {
case MotionEvent.ACTION_DOWN: // touch down so check if the finger is on a ball
balID = 0;
for (ColorBall ball : colorballs) {
// check if inside the bounds of the ball (circle)
// get the center for the ball
int centerX = ball.getX() + 25;
int centerY = ball.getY() + 25;
// calculate the radius from the touch to the center of the ball
double radCircle = Math.sqrt( (double) (((centerX-X)*(centerX-X)) + (centerY-Y)*(centerY-Y)));
// if the radius is smaller then 23 (radius of a ball is 22), then it must be on the ball
if (radCircle < 23){
balID = ball.getID();
break;
}
// check all the bounds of the ball (square)
//if (X > ball.getX() && X < ball.getX()+50 && Y > ball.getY() && Y < ball.getY()+50){
// balID = ball.getID();
// break;
//}
}
break;
case MotionEvent.ACTION_MOVE: // touch drag with the ball
// move the balls the same as the finger
if (balID > 0) {
Log.v("Images","3333 Moving");
colorballs[balID-1].setX(X-25);
colorballs[balID-1].setY(Y-25);
}
break;
case MotionEvent.ACTION_UP:
/*for (ColorBall ball : colorballs) {
Log.v("y value","YYYYYYYYYYY "+ball.getY()+"XXXXXXXXXXXX "+ball.getID());
}*/
// touch drop - just do things here after dropping
//setFocusable(false);
break;
}
// redraw the canvas
invalidate();
return true;
}
}
Hi I am using the above code for displaying bitmap.and i also move that bitmap.Now my qusetion is how i am compare bitmap with another bitmap.
Please give me some suggestions.Thanks in advance
"collision detection" is what you should look after. There are infinite algorithms for that out there.. one on SO: Collision Detection between two images in Java
bitmaps are equal when we move one bimap near to anther bitmap and if both are same the bitmap moving become false
Assuming you want to do Collision detection between two circles, this can be performed by getting the distance between the 2 centres. EG:
double getDistance(Point p1, Point p2){
double dx = p1.x - p2.x;
double dy = p1.y - p2.y;
return Math.sqrt((dx*dx)+(dy*dy));
}
Then checking if the value is less than the radius of circle1 + the radius of circle2, which would be faster then checking the images for overlap.