I understand that this is technically a repeat question, but all of the similar questions include code I do not understand, so I decided to ask the question using code I understand.
I am attempting to make a flappy bird style game to try android programing and I cannot get Rect.intersects to change the player's score when the player collides with an object (cloud)
Thanks in advance!!
View class:
package com.gregsapps.fallingbird;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
public class GameView extends View{
private Bird bird;
private boolean runOnce = false;
private Context context;
private Paint red;
ArrayList<Cloud> clouds = new ArrayList<Cloud>();
private int cloudDelay;
private Collision collision;
//private Paint textPaint;
public GameView(Context context) {
super(context);
this.context = context;
this.setDrawingCacheEnabled(true);
red = new Paint();
red.setColor(Color.RED);
red.setTextSize(100f);
collision = new Collision();
//textPaint.setColor(Color.BLACK);
//textPaint.setTextAlign(Align.RIGHT);
// TODO add setup code
}
protected void onDraw(Canvas canvas){
update(canvas);
System.out.println(bird.score);
//TODO add drawing code
this.buildDrawingCache();
//bird.canvasImage = this.getDrawingCache(true);
canvas.drawColor(Color.rgb(10, 255, 255));
canvas.drawRect(bird.birdRect, red);
canvas.drawBitmap(bird.image, bird.x, bird.y, null);
for(int i = 0; i < clouds.size(); i++){
System.out.println("Drawing cloud");
Cloud cloud = (Cloud) clouds.get(i);
cloud.move(5);
System.out.println(cloud.leftX + "/t" + cloud.rightX);
canvas.drawRect(cloud.rightCloud, red);
canvas.drawRect(cloud.leftCloud, red);
canvas.drawBitmap(cloud.image, cloud.leftX, cloud.leftY, null);
canvas.drawBitmap(cloud.image, cloud.rightX, cloud.rightY, null);
if(cloud.leftY <= -144)clouds.remove(cloud);
if(bird.y > cloud.leftY + bird.height) bird.score++;
if(Rect.intersects(bird.birdRect, cloud.leftCloud)){
bird.score = 0;
}
else if(Rect.intersects(bird.birdRect, cloud.rightCloud)){
bird.score = 0;
}
}
canvas.drawLine(canvas.getWidth()/2 - 1, 0, canvas.getWidth()/2 - 1, canvas.getHeight(), red);
cloudDelay --;
if(cloudDelay <= 0){
System.out.println("new cloud");
Cloud cloud = new Cloud(com.gregsapps.fallingbird.R.drawable.cloud, context);
System.out.println("added");
clouds.add(cloud);
cloudDelay = 175;
}
canvas.drawText(Integer.toString(bird.score/12), 50, 100, red);
invalidate();
}
private void update(Canvas canvas){
//TODO add code to update stuff
if(runOnce == false){
bird = new Bird(canvas, com.gregsapps.fallingbird.R.drawable.bird, context);
runOnce = true;
StaticVarHandler.canvasHeight = canvas.getHeight();
StaticVarHandler.canvasWidth = canvas.getWidth();
}
bird.move();
}
}
Cloud class:
package com.gregsapps.fallingbird;
import java.util.Random;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
public class Cloud {
public int leftX;
public int leftY;
public int rightX;
public int rightY;
private Random random;
public Bitmap image;
private Context context;
public Rect leftCloud;
public Rect rightCloud;
public int height;
public int width;
Cloud(int image, Context context){
this.context = context;
this.image = BitmapFactory.decodeResource(this.context.getResources(), R.drawable.cloud);
random = new Random();
leftX = random.nextInt(StaticVarHandler.canvasWidth-(StaticVarHandler.birdWidth*2));
rightX = leftX + StaticVarHandler.birdWidth*2;
rightY = leftY = StaticVarHandler.canvasHeight+this.image.getHeight();
leftX -= this.image.getWidth();
leftCloud = new Rect(leftX, leftY, this.image.getWidth(), this.image.getHeight());
rightCloud = new Rect(rightX, rightY, this.image.getWidth(), this.image.getHeight());
}
public void move(int scrollSpeed){
leftCloud.offset(0, -scrollSpeed);
rightCloud.offset(0, -scrollSpeed);
leftY-=scrollSpeed;
rightY-=scrollSpeed;
}
}
Bird class:
package com.gregsapps.fallingbird;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
public class Bird {
public int x;
public int y;
private int speed;
public Bitmap image;
Context context;
private int gravity;
public int width;
public int height;
private int canvasWidth;
private int canvasHeight;
public Bitmap canvasImage;
public boolean touch;
public int touchX;
public int touchY;
private int gravDelay = 0;
private int jump = 0;
public int score;
public Rect birdRect;
Bird(Canvas canvas, int image, Context context){
this.context = context;
this.image = BitmapFactory.decodeResource(this.context.getResources(), image);
x = canvas.getWidth()/2 - this.image.getWidth()/2;
y = 10;
speed = this.image.getWidth()/10;
//setup gravity, speed, width and height attributes
speed = canvas.getWidth()/25;
gravity = speed/10;
StaticVarHandler.birdWidth = width = this.image.getWidth();
height = this.image.getHeight();
canvasWidth = canvas.getWidth();
canvasHeight = canvas.getHeight();
System.out.println(canvasWidth);
System.out.println(canvas.getWidth());
birdRect = new Rect(x, y, this.image.getWidth(), this.image.getHeight());
}
public void move(){
gravDelay --;
jump --;
if(StaticVarHandler.touch) jump = 3;
if(jump >= 0){
if(StaticVarHandler.touchX < canvasWidth/2){
x -= speed/3;
}
if(StaticVarHandler.touchX > canvasWidth/2){
x += speed/3;
}
StaticVarHandler.touch = false;
if(jump == 0) gravDelay = 7;
}
else if(gravDelay <= 0){
System.out.println("GRAVITY");
if(x+width/2 < canvasWidth/2){
x += gravity;
//code to move bird via gravity
}
if(x+width/2 > canvasWidth/2){
x -= gravity;
//same as above but other side
}
gravDelay = 1;
}
if(x <= 0){
score = 0;
x = 0;
}
else if(x+width >= canvasWidth){
score = 0;
x = canvasWidth - width;
}
birdRect.offsetTo(x-1, y-1);
}
private void collisionCheck(){
if(longEquation()){
}
}
}
I think I found your problem.
birdRect = new Rect(x, y, this.image.getWidth(), this.image.getHeight());
and
leftCloud = new Rect(leftX, leftY, this.image.getWidth(), this.image.getHeight());
rightCloud = new Rect(rightX, rightY, this.image.getWidth(), this.image.getHeight());
Should not use getWidth() or getHeight() but should use x + width and y + height.
public Rect (int left, int top, int right, int bottom)
Added in API level 1 Create a new rectangle with the specified
coordinates. Note: no range checking is performed, so the caller must
ensure that left <= right and top <= bottom.
Parameters
left The X coordinate of the left side of the rectangle
top The Y coordinate of the top of the rectangle
right The X
coordinate of the right side of the rectangle
bottom The Y coordinate
of the bottom of the rectangle
This is from the documentation.
Related
I am a very beginner programmer, so I follow a youtube-tutorial that shows me how to build a simple snake app. The men at the youtube-tutorial got an older version of AndroidStudio than me. He uses AndroidStudio version 2.1.2, I use AndroidStudio version 2.2.2 with java language.
My problem is that he uses the commands getMap and map and it works. When I do that it doesn't work.My guestion is what i have to replace in the >methods "getMap" and "map"
The youtube-tutorial that I follow: https://www.youtube.com/watch?v=bPlG7ra83lo
At 12:00 he uses this command first time.
Game Engine
package pelgrom.laurens.snake101.engine;
import android.service.quicksettings.Tile;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import pelgrom.laurens.snake101.Classes.Coordinate;
import pelgrom.laurens.snake101.enums.TileType;
/**
* Created by Laptop on 23-1-2017.
*/
public class GameEngine {
public static final int GameWidth = 28;
public static final int Gameheight = 42;
private List<Coordinate> walls = new ArrayList<>();
public GameEngine() {
}
public void initGame(){
AddWalls();
}
public TileType()() getMap() {
TileType()() map = new TileType(GameWidth) (Gameheight);
for (int x = 0; x < GameWidth; x++) {
for (int y = 0; y < Gameheight; y++) {
map(X)(Y) = TileType.Nothing;
}
}
for (Coordinate wall: walls) {
map(wall.getX())(wall.getY()) = TileType.Wall;
}
return map;
}
//fout zit hem in de "map" en de "getMap"
private void AddWalls() {
//Top and bottom walls
for (int x = 0; x < GameWidth; x ++) {
walls.add(new Coordinate(x,0));
walls.add(new Coordinate(x,Gameheight-1));
}
//Left and Right walls
for (int y = 1; y < Gameheight; y++){
walls.add(new Coordinate(0,y));
walls.add (new Coordinate(GameWidth -1 , y));
}
}
}
`
SnakeView
`package pelgrom.laurens.snake101.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import pelgrom.laurens.snake101.enums.TileType;
/**
* Created by Laptop on 23-1-2017.
*/
public class SnakeView extends View {
private Paint mPaint = new Paint();
private TileType snakeViewMap()();
public SnakeView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setSnakeViewMap(TileType()() map )( this.snakeViewMap = map; )
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if( snakeViewMap != null ){
float tileSizeX = canvas.getWidth() / snakeViewMap.length;
float tileSizeY = canvas.getHeight() / snakeViewMap.length;
float circleSize = Math.min(tileSizeX, tileSizeY) / 2;
for (int x = 0; x < snakeViewMap.lenght; x++) {
for (int y = 0; y < snakeViewMap(x). lenght; y++) {
switch (snakeViewMap(x)) {
case Nothing:
mPaint.setColor(Color.WHITE);
break;
case Wall:
mPaint.setColor(Color.GREEN);
break;
case SnakeHead:
mPaint.setColor(Color.RED);
break;
case SnakeTail:
mPaint.setColor(Color.GREEN);
break;
case Apple:
mPaint.setColor(Color.RED);
break;
}
canvas.drawCircle(x * tileSizeX + tileSizeX / 2f + circleSize / 2, y * tileSizeY + tileSizeY / 2f + circleSize / 2, circleSize, mPaint);
}
}
}
}
}
`
main activity
`package pelgrom.laurens.snake101;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import pelgrom.laurens.snake101.engine.GameEngine;
import pelgrom.laurens.snake101.views.SnakeView;
public class Activity extends AppCompatActivity {
private GameEngine gameEngine;
private SnakeView snakeView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout);
gameEngine = new GameEngine();
gameEngine.initGame();
snakeView = (SnakeView)findViewById(R.id.snakeView);
snakeView.setSnakeViewMap(gameEngine.getMapAsync());
snakeView.invalidate();
}
}
`
The problem with your code is that you are using Round Bracket () while it has to square brackets [] since you are declaring two dimensional arrays. Replace them like :
public TileType[][] getMap() {
TileType[][] map = new TileType[GameWidth][Gameheight];
for (int x = 0; x < GameWidth; x++) {
for (int y = 0; y < Gameheight; y++) {
map[x][y] = TileType.Nothing;
}
}
for (Coordinate wall: walls) {
map[wall.getX()][wall.getY()] = TileType.Wall;
}
return map;
}
PS: you really need to start with the basics.
I want to make a game wherein when the main character sprite's X coordinate is less than the middle of the screen, he moves to the right and when it's more than the middle of the screen, he moves to the left. The sprite's animation changes when he is moving and when he is still (after reaching its destination). What I want to know is how can I do this when the sprite and its code for animation is in one class and the code for changing its X coordinate is in another class? Is it best to draw the same sprite in every class? How can I change the sprite when it is moving horizontally and when it is still? I plan to separate the code for what the sprite's actions will be in different classes because i want to call them randomly. Here is my code for the sprite's animation and i have no class for changing the x coordinate yet :
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.jpfalmazan.ninjaassault.NinjaAssault;
public class MainScreen implements Screen {
private static final int FRAME_COLS = 3;
private static final int FRAME_ROWS = 2;
Animation walkAnimation;
Texture walkSheet = new Texture ("ninjaWalk.png");
TextureRegion[] walkFrames;
TextureRegion currentFrame;
Texture holdStart;
float stateTime;
public Texture background;
private NinjaAssault game;
private Music BackgroundSFX;
public float screenWidth = Gdx.graphics.getWidth();
public float screenHeight = Gdx.graphics.getHeight();
float x = screenWidth/2;
float y = screenHeight/2;
public float walkSheetWidth = walkSheet.getWidth();
public float walkSheetHeight = walkSheet.getHeight();
public MainScreen (NinjaAssault game){
this.game = game;
}
#Override
public void show(){
background = new Texture("BGBlue.png");
holdStart = new Texture ("HoldStart.png");
BackgroundSFX = Gdx.audio.newMusic(Gdx.files.internal("data/RADWIMPS-iindesuka.mp3"));
TextureRegion[][] tmp = TextureRegion.split(walkSheet, (int )walkSheetWidth/FRAME_COLS, (int) walkSheetHeight/FRAME_ROWS);
walkFrames = new TextureRegion[FRAME_COLS*FRAME_ROWS];
int index = 0;
for (int i = 0; i < FRAME_ROWS; i++){
for (int j = 0; j < FRAME_COLS; j++) {
walkFrames[index++] = tmp[i][j];
}
}
walkAnimation = new Animation(0.0887f, walkFrames);
stateTime = 0f;
}
#Override
public void render(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stateTime += Gdx.graphics.getDeltaTime();
currentFrame = walkAnimation.getKeyFrame(stateTime, true);
float hsWidth = holdStart.getWidth();
float hsHeight = holdStart.getHeight();
float currentFrameWidth = (float)(screenHeight*0.15);
float currentFrameHeight = (float)(screenHeight*0.15);
float holdStartWidth = (float)(screenWidth * 0.75);
game.batch.begin();
game.batch.draw(background,0,0, screenWidth,screenHeight);
BackgroundSFX.play();
game.batch.draw(currentFrame, x -currentFrameWidth/2, 0,currentFrameWidth,currentFrameHeight);
game.batch.draw(holdStart, (screenWidth / 2 - (holdStartWidth / 2)), (float) (screenHeight * 0.5), holdStartWidth, holdStartWidth * (hsHeight / hsWidth));
game.batch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
BackgroundSFX.dispose();
background.dispose();
walkSheet.dispose();
holdStart.dispose();
}
}
I tried to research on how to do this but the answers I get wasn't that helpful.
Simplest way is to create a Player class and put all the code for the animation and frames in there. Then give it a way to save its position. Like a vector or just a float for the x coordinate. Or even better, use a Rectangle. A rectangle will make moving and collision detection much easier.
Something like this:
public class Player{
private Animation walkAnimation;
private Texture walkSheet;
private TextureRegion[] walkFrames;
private TextureRegion currentFrame;
private float stateTime;
private Rectangle bound; //used for positioning and collision detection
public Player(float x, float y, float width, float height){
bound = new Rectangle();
bound.x = x;
bound.y = y;
bound.width = width;
bound.height = height;
walkSheet = new Texture ("ninjaWalk.png");
TextureRegion[][] tmp = TextureRegion.split(walkSheet,(int)walkSheetWidth/FRAME_COLS, (int) walkSheetHeight/FRAME_ROWS);
walkFrames = new TextureRegion[FRAME_COLS*FRAME_ROWS];
int index = 0;
for (int i = 0; i < FRAME_ROWS; i++){
for (int j = 0; j < FRAME_COLS; j++) {
walkFrames[index++] = tmp[i][j];
}
}
walkAnimation = new Animation(0.0887f, walkFrames);
stateTime = 0f;
}
public rectangle getBound(){
return bound;
}
public void update(float delta){
statetime += delta;
currentFrame = walkAnimation.getKeyFrame(stateTime, true);
}
public TextureRegion getCurrentFrame(){
return currentFrame;
}
}
This is just a quick untested example.
You say you want to move the player from another class. I don't know how you plan to do that, but all you need to do to move the player is to manipulate the x and y of the bound.
Just some other comments on you code:
#Override
public void render(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
player.update(delta); // to update the player
/***
* This does not have to be set every single frame. Move it to show()
*
float hsWidth = holdStart.getWidth();
float hsHeight = holdStart.getHeight();
float currentFrameWidth = (float)(screenHeight*0.15);
float currentFrameHeight = (float)(screenHeight*0.15);
float holdStartWidth = (float)(screenWidth * 0.75);
****************************************************/
BackgroundSFX.play(); // I am sure you don't need to start playing this every single frame? 60 times a second.
game.batch.begin();
game.batch.draw(background,0,0, screenWidth,screenHeight);
game.batch.draw(player.getCurrentFrame(), player.getBound().x, player.getbound().y, player.getBound().width, player.getBound().height)
game.batch.draw(holdStart, (screenWidth / 2 - (holdStartWidth / 2)), (float) (screenHeight * 0.5), holdStartWidth, holdStartWidth * (hsHeight / hsWidth));
game.batch.end();
}
i am trying to implement Deceleration on the bouncy ball. i am able to move the ball up and down but i wanted to add Declaration so when i drop the ball from height it jumps then strike the ground bounce once again and slowly slowly its speed decreases and stop at the end.
Here is My code
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.widget.ImageView;
public class AnimatedView extends ImageView{
private Context mContext;
int x = -1;
int y = -1;
private int xVelocity = 0;
private int yVelocity = 25;
private Handler h;
private final int FRAME_RATE = 20;
public AnimatedView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
h = new Handler();
}
private Runnable r = new Runnable() {
#Override
public void run() {
invalidate();
}
};
protected void onDraw(Canvas c) {
BitmapDrawable ball = (BitmapDrawable) mContext.getResources().getDrawable(R.drawable.ball);
if (x<0 && y <0) {
x = this.getWidth()/2;
y = this.getHeight()/2;
} else {
x += xVelocity;
y += yVelocity;
if ((x > this.getWidth() - ball.getBitmap().getWidth()) || (x < 0)) {
xVelocity = xVelocity*-1;
}
if ((y > this.getHeight() - ball.getBitmap().getHeight()) || (y < 0)) {
yVelocity = yVelocity*-1;
}
}
c.drawBitmap(ball.getBitmap(), x, y, null);
h.postDelayed(r, FRAME_RATE);
}
Hope Anyone Can Help me.
Thanks in Advance
Acceleration is to velocity as velocity is to position. You just need to do what you're doing with your position to your velocity.
The simplest solution is to add
yVelocity += yAccel;
to the top of onDraw where yAccel is a int that you want added to the yVelocity every tick. Based on your velocity values
private int yAccel = -2;
might be appropriate.
edit: You should also add checks to ensure the ball does not go into the ground. I added these in the blocks that reverse the velocity, the following code works for me. If it don't work for you, there might be something wrong with another part of your project.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.widget.ImageView;
public class AnimatedView extends ImageView {
private Context mContext;
int x = -1;
int y = -1;
private int xVelocity = 0;
private int yVelocity = 25;
private int yAccel = 2;
private Handler h;
private final int FRAME_RATE = 20;
public AnimatedView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
h = new Handler();
}
private Runnable r = new Runnable() {
#Override
public void run() {
invalidate();
}
};
protected void onDraw(Canvas c) {
yVelocity += yAccel;
BitmapDrawable ball = (BitmapDrawable) mContext.getResources()
.getDrawable(R.drawable.cakes);
if (x < 0 && y < 0) {
x = this.getWidth() / 2;
y = this.getHeight() / 2;
} else {
x += xVelocity;
y += yVelocity;
if ((x > this.getWidth() - ball.getBitmap().getWidth()) || (x < 0)) {
xVelocity = xVelocity * -1;
x = Math.min(this.getWidth() - ball.getBitmap().getWidth(), x);
x = Math.max(0, x);
}
if ((y > this.getHeight() - ball.getBitmap().getHeight())
|| (y < 0)) {
yVelocity = (int)(yVelocity * -0.8f);
y = Math.min(this.getHeight() - ball.getBitmap().getHeight(), y);
y = Math.max(0, y);
}
}
c.drawBitmap(ball.getBitmap(), x, y, null);
h.postDelayed(r, FRAME_RATE);
}
}
Can anyone help, my app does not seem to do the onDraw() in the GraView class, even if .invalidate is called upon from either inside or outside the class. I managed to figure out with system.err that it does invalidate the view, however it never gets in the onDraw( ), even though the rest of the app keeps running. I found a lot of solutionssuggesting putting setWillNotDraw(false) in the constructor, but that did not solve anything.
GravIO.java File:
package dabawi.gravitas;
import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class GravIO extends Activity implements Runnable, OnTouchListener {
public final static int clock = 1000;
private GravEngine engine;
private GraView TV;
private SensorManager mSensorManager;
private Sensor mSensor;
private Thread t1;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
t1 = new Thread(this);
t1.start();
engine = new GravEngine();
TV = new GraView(this, engine);
setContentView(TV);
TV.setOnTouchListener(this);
TV.setVisibility(View.VISIBLE);
//mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
System.err.println("Starting Engine") ;
run();
}
#Override
public void run() {
while (true) {
try {
System.err.println("Tik") ;
engine.tick();
TV.invalidate();
Thread.sleep(GravIO.clock);
} catch (InterruptedException ex) {
System.err.println("faal");
}
}
}
#Override
public boolean onTouch(View view, MotionEvent event) {
engine.switchGravity();
return true;
}
}
GraView.java File:
package dabawi.gravitas;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
#SuppressLint({ "DrawAllocation", "ViewConstructor" })
public class GraView extends View {
private GravEngine engine;
private int screenWidth, screenHeight, ySpace = 5, xSpace, scale;
private Paint paint;
private int xLoc, yLoc;
private boolean xWall = false, yWall = false;
public GraView(Context context, GravEngine engine) {
super(context);
setWillNotDraw(false);
this.engine = engine;
calcScale(context);
}
#Override // We think the problem is with this method, that it is never called upon.
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
System.err.println("Calculating position");
calcPos();
canvas = new Canvas();
// drawing background
paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
canvas.drawPaint(paint);
drawGame(canvas);
}
private void drawGame(Canvas canvas) {
drawRoom(canvas);
drawPlayer(canvas);
}
#SuppressWarnings("deprecation")
private void calcScale(Context context) {
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
Display d = wm.getDefaultDisplay();
screenWidth = d.getWidth();
screenHeight = d.getHeight();
scale = screenHeight / ySpace;
xSpace = screenWidth / scale;
}
private void calcPos() {
xLoc = (engine.getxPlayer() / GravEngine.roomScaling);
yLoc = (engine.getyPlayer() / GravEngine.roomScaling);
if (xLoc < (xSpace + 1) / 2) {
xLoc = (xSpace + 1) / 2;
xWall = true;
} else if (xLoc > (GravRoom.xSize - ((xSpace + 1) / 2))) {
xLoc = GravRoom.xSize - ((xSpace + 1) / 2);
xWall = true;
} else {
xWall = false;
}
if (yLoc < (ySpace + 1) / 2) {
yLoc = (ySpace + 1) / 2;
yWall = true;
} else if (yLoc > (GravRoom.xSize - ((ySpace + 1) / 2))) {
xLoc = GravRoom.ySize - ((ySpace + 1) / 2);
yWall = true;
} else {
yWall = false;
}
}
private void drawPlayer(Canvas canvas) {
float xPos = engine.getxPlayer() / GravEngine.roomScaling, yPos = engine
.getyPlayer() / GravEngine.roomScaling;
if (xWall) {
}
if (yWall) {
}
paint.setColor(Color.BLUE);
int left = (int) (xPos * scale), top = (int) (yPos * scale);
int right = left + (GravEngine.pxSize / GravEngine.roomScaling) * scale;
int bot = top + (GravEngine.pySize / GravEngine.roomScaling) * scale;
canvas.drawRect(left, top, right, bot, paint);
}
private void drawRoom(Canvas canvas) {
for (int i = 0, x = xLoc - ((xSpace + 1) / 2); i < xSpace + 1; x++, i++) {
for (int j = 0, y = yLoc - ((ySpace + 1) / 2); i < ySpace + 1; y++, j++) {
drawRoomPart(x,y,i,j,canvas);
}
}
}
private void drawRoomPart(int x, int y, int i, int j, Canvas canvas) {
if (x >= 0 && y >= 0 && x < GravRoom.xSize && y < GravRoom.ySize) {
short type = engine.getRoom(engine.getCurrentRoom()).getGridPos(x,y);
if (type != 0) {
drawBlock(canvas, i, x, j, y, type);
}
}
}
private void drawBlock(Canvas canvas, int i, int x, int j, int y, short type) {
int left = i * scale, top = y * scale;
int right = left + scale;
int bot = top + scale;
paint.setColor(colorBlock(type));
canvas.drawRect(left, top, right, bot, paint);
System.err.println("Left" + left + " top: " + top + " right: "
+ right + " bot: " + bot);
}
private int colorBlock(short type) {
if (type == 1) {
return Color.DKGRAY;
} else if (type == 2) {
return Color.CYAN;
} else if (type == 3) {
return Color.GREEN;
} else if (type == 4) {
return Color.RED;
} else {
return Color.MAGENTA;
}
}
// private void imageBlock(short type) {
//
// }
}
Ouch !
Your call to your run method is executed in the UIThread (as the onCreate method), and it's doing an infinite loop in it.
Just add a println after the call and you will see that the UI thread is never released, your application should not even start !
You don't see any changes because you are creating a new canvas and drawing on that. To fix this, remove the line
canvas = new Canvas();
Also, you should initiate your Paint outside of the onDraw method (this is an optimization recommended by the Android documentation).
My custom view looks as below
package com.mypackage;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.widget.ImageView;
public class CustomDrawableView extends ImageView {
// private ShapeDrawable mDrawable;
public int imageid = 0;
ShapeDrawable bgDrawable = null;
List<ShapeDrawable> ls = new ArrayList<ShapeDrawable>();
final int COUNT_SUMMERY = 3;
final int VERTICAL_OFFSET = 200;
final int HORIZENTAL_OFFSET = 20;
final int HORIZENTAL_GAP = 85;
final int VERTICAL_Y_POINT = 15;
final int VERTICAL_MAX_HEIGHT = 195;
final int HORIZENTAL_WIDTH = 60;
final int percentage[] = {25, 40, 35};
public CustomDrawableView(Context context, int id) {
super(context);
imageid = id;
switch(id) {
case R.drawable.summarychart:
for(int i = 0; i < COUNT_SUMMERY; i++) {
int x = HORIZENTAL_OFFSET + (HORIZENTAL_GAP * i);
int width = HORIZENTAL_WIDTH;
int height = VERTICAL_MAX_HEIGHT * percentage[i] / 100;
int y = VERTICAL_OFFSET + VERTICAL_Y_POINT + VERTICAL_MAX_HEIGHT - height;
ShapeDrawable objDrawable = new ShapeDrawable(new RectShape());
int color = 0;
switch(i) {
case 0:
color = Color.RED;
break;
case 1:
color = Color.GREEN;
break;
case 2:
color = Color.YELLOW;
break;
}
objDrawable.getPaint().setColor(color);
objDrawable.setBounds(x, y, x + width, y + height);
ls.add(objDrawable);
}
break;
default:
break;
}
int x = 0;
int y = 0;
int width = 320;
int height = 480;
bgDrawable = new ShapeDrawable(new RectShape());
bgDrawable.getPaint().setColor(0xffffffff);
bgDrawable.setBounds(x, y, x + width, y + height);
}
protected void onDraw(Canvas canvas) {
/* // Draw the white background
// bgDrawable.draw(canvas);
// Draw the bitmaps
Bitmap bmp = BitmapFactory.decodeResource(getResources(), imageid);
canvas.drawBitmap(bmp, 0, VERTICAL_OFFSET, null);
*/ // Draw bars
super.onDraw(canvas);
for(int i = 0; i < COUNT_SUMMERY; i++) {
ShapeDrawable objDrawable = ls.get(i);
objDrawable.draw(canvas);
}
}
}
And in the layout I have:
<com.mypackage.CustomDrawableView
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:src="#drawable/mychart" />
However I see the message:
NoSuchMethodException:com.mypackage.CustomDrawableView.(Android.context.Context, Android.util.AttributeSet)
and if I try to run it the application crashes. If I uncomment the drawable way, then it works, but other layouts are not displayed.
Any help is appreciated.
You can fix it by implementing your missing constructor. When you try to instantiate this from XML it is invoked.
public CustomDrawableView(Context context, AttributeSet attrs) {
super(context, attrs);
}
All you need to do is look at the exception you are seeing.
NoSuchMethodException:com.mypackage.CustomDrawableView.(Android.context.Context,
Android.util.AttributeSet)
It means that there is no method with the signature com.mypackage.CustomDrawableView.(Android.context.Context, Android.util.AttributeSet) because you haven't overridden it in your class.