Android: Background on SurfaceView? - android

I have a surface view, and whenever I touch the screen, an image comes up in the spot I touch. It's great, but I cannot figure out how to put a background on the SurfaceView. I have tried using the OnDraw to draw a background right away (Without having to touch it), and that only works some of the time. It force closes most of the time.
Would anyone want to look at my code and see if it's possible to get a background image on the Surface view? Thanks in advance.
class MyView extends SurfaceView implements SurfaceHolder.Callback {
private Thready _thread;
private ArrayList _graphicsz = new ArrayList();
private GraphicObject _currentGraphic = null;
public MyView(Context context) {
super(context);
getHolder().addCallback(this);
_thread = new Thready(getHolder(), this);
setFocusable(true);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (_thread.getSurfaceHolder()) {
GraphicObject graphic = null;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
graphic = new GraphicObject(BitmapFactory.decodeResource(getResources(), R.drawable.cat1small));
graphic.getCoordinates().setX((int) event.getX() - graphic.getGraphic().getWidth() / 2);
graphic.getCoordinates().setY((int) event.getY() - graphic.getGraphic().getHeight() / 2);
_currentGraphic = graphic;
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
_currentGraphic.getCoordinates().setX((int) event.getX() - _currentGraphic.getGraphic().getWidth() / 2);
_currentGraphic.getCoordinates().setY((int) event.getY() - _currentGraphic.getGraphic().getHeight() / 2);
} else if (event.getAction() == MotionEvent.ACTION_UP) {
_graphicsz.add(_currentGraphic);
_currentGraphic = null;
}
return true;
}
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
Bitmap bitmap1;
GraphicObject.Coordinates coords1;
for (GraphicObject graphic : _graphicsz) {
bitmap1 = graphic.getGraphic();
coords1 = graphic.getCoordinates();
canvas.drawBitmap(bitmap1, coords1.getX(), coords1.getY(), null);
}
// draw current graphic at last...
if (_currentGraphic != null) {
bitmap1 = _currentGraphic.getGraphic();
coords1 = _currentGraphic.getCoordinates();
canvas.drawBitmap(bitmap1, coords1.getX(), coords1.getY(), null);
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}
public void surfaceCreated(SurfaceHolder holder) {
_thread.setRunning(true);
_thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
// simply copied from sample application LunarLander:
// we have to tell thread to shut down & wait for it to finish, or else
// it might touch the Surface after we return and explode
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
}
class Thready extends Thread {
private SurfaceHolder _surfaceHolder;
private MyView _panel;
private boolean _run = false;
public Thready(SurfaceHolder surfaceHolder, MyView panel) {
_surfaceHolder = surfaceHolder;
_panel = panel;
}
public void setRunning(boolean run) {
_run = run;
}
public SurfaceHolder getSurfaceHolder() {
return _surfaceHolder;
}
#Override
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
_panel.onDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
class GraphicObject {
/**
* Contains the coordinates of the graphic.
*/
public class Coordinates {
private int _x = 100;
private int _y = 0;
public int getX() {
return _x + _bitmap.getWidth() / 2;
}
public void setX(int value) {
_x = value - _bitmap.getWidth() / 2;
}
public int getY() {
return _y + _bitmap.getHeight() / 2;
}
public void setY(int value) {
_y = value - _bitmap.getHeight() / 2;
}
public String toString() {
return "Coordinates: (" + _x + "/" + _y + ")";
}
}
private Bitmap _bitmap;
private Coordinates _coordinates;
public GraphicObject(Bitmap bitmap) {
_bitmap = bitmap;
_coordinates = new Coordinates();
}
public Bitmap getGraphic() {
return _bitmap;
}
public Coordinates getCoordinates() {
return _coordinates;
}
}

Well, what strikes me first off all (and what could be causing a lot of problem) is this:
you're creating new bitmap way too often
Every time you get a touch event, you're loading in your bitmaps ... and you can get 80 to 100 touchevents every second!
So to start with, define global bitmaps (private Bitmap bmp1; etc), load them somewhere else and THEN use them in your touch event.
Also, remove the bmp loading/creation from onDraw and move it to somewhere else.
After you've done that, see what happens; you might have more problems (a REALY quick scan of your code seemed fine), but not creating and loading bitmaps 80 times a second will definitely help out :)

Related

Android app crashes upon attempt to switch to a different Activity

I am running into an issue in my app. When my game ends (when life == 0) I am attempting to switch to a game over screen by using a different activity. When the game ends, the app simply crashes. I have included the XML for the activity I am trying to switch from as well as indicating where the app crashes. If anyone could help out, that would be great! Thanks.
activity_game.XML:
SurfaceView I am trying to switch from once game ends:
public class SVGameView extends SurfaceView implements Runnable {
private SurfaceHolder holder;
Thread thread = null;
volatile boolean running = false;
static final long FPS = 30;
private Sprite sprite;
private long lastClick;
private Bitmap ball, gameOver;
//private int x = 200, y = 200;
private int scorePosX = 100;
private int scorePosY = 100;
private int countScore = 0;
private int life = 1;
public SVGameView(Context context) {
super(context);
thread = new Thread(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
running = false;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
running = true;
thread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
ball = BitmapFactory.decodeResource(getResources(), R.drawable.ball2);
gameOver = BitmapFactory.decodeResource(getResources(),R.drawable.endscreen);
sprite = new Sprite(this, ball);
}
#Override
public void run() {
long ticksPS = 1000 / FPS;
long startTime;
long sleepTime;
while (running) {
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = getHolder().lockCanvas();
synchronized (getHolder()) {
update();
draw(c);
}
} finally {
if (c != null) {
getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
thread.sleep(sleepTime);
else
thread.sleep(10);
} catch (Exception e) {}
}
}
private void update(){
sprite.update();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
canvas.drawPaint(paint);
paint.setColor(Color.WHITE);
paint.setTextSize(48);
canvas.drawText("Score: " + countScore, scorePosX, scorePosY, paint);
canvas.drawText("Lives: " + life, 500, 100, paint);
sprite.onDraw(canvas);
//Crashes here
if(life == 0) {
getContext().startActivity(new Intent(getContext(), SVGameOver.class));
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(System.currentTimeMillis()-lastClick > 300){
lastClick = System.currentTimeMillis();
}
synchronized (getHolder()){
if(sprite.isHit(event.getX(), event.getY())){
countScore += 1;
sprite.increase();
}else{
life --;
}
}
return super.onTouchEvent(event);
}
}
Activity I am trying to reach once the game ends:
public class SVGameOver extends Activity {
private Bitmap gameOverScreen;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
gameOverScreen = BitmapFactory.decodeResource(getResources(), R.drawable.endscreen);
}
protected void onDraw(Canvas canvas){
canvas.drawBitmap(gameOverScreen, 0,0,null);
}
}
I think logcat is asking you the right question: "have you declared this activity in your AndroidManifest.xml" ?
If you think you did it, It's highly probable you did it in a wrong way, most of the times that you think you added an Activity to the manifest but you are receiving this kind of crash, 99,9% of the time you declared it with a wrong namespace
Declare SVGameOver activity in your AndroidManifest.xml:
<activity
android:name="com.example.welcome.assignment2.SVGameOver">
...
</activity>

Android, terminate threads when activity paused or stopped?

I have an activity like below. I want the threads inside the activity terminate when the activity is paused or stopped. I searched and found volatile Boolean solution which didn't work for me. When i put the activity in pause or stop state, download continues, which i don't want it.
public class MyActivity extends Activity {
//some code here
private void foo(){
new Thread (new Runnable (){
#Override
public void run() {
//download something from internet
}
}).start();
}
}
i used this pattern which didn't work:
public class MyActivity extends Activity {
volatile Boolean state = true;
//some code here
private void foo(){
new Thread (new Runnable (){
#Override
public void run() {
while (state) {
//download something from internet
}
}
}).start();
}
#Override
public void onPause(){
super.onPause();
state = false;
}
#Override
public void onStop(){
super.onStop();
state = false;
}
}
Here's an example of the structure of a thread which stops when back is hit, which I made for my game (and works). One key difference is you're using a thread in your Activity, while in mine I call a View in my Activity and run the thread in the View. If I hit back, I return to my Activity, and can call the thread again by hitting 'start.' BECAUSE my thread behaves the way you want yours too, even if it's happening in View, I thought you may find it helpful.
Where my of my synchronization happens is with the touchscreen values, and making sure those are updated in the thread and calling function.
The thread is trashed totally unless you have a way of saving the state (if you need to).
Your thread will need to synchronize with other functions you want controlling/sharing values with the thread, like onPause, etc..
**you'll need to have some synchronized test value inside your while loop which can then change state to false, otherwise the thread will continue on its own, which is the point of threads.
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
private final GameActivity gameActivity;
private GameThread _thread;
private boolean previouslyRunning = false;
private int width; //Screen width
private int height; //Screen height
private boolean newGame = true;
public GameView(Context context) {
super(context);
getHolder().addCallback(this);
this.gameActivity = (GameActivity) context;
_thread = new GameThread(getHolder(), this);
setFocusable(true);
setFocusableInTouchMode(true);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
width = w;
height = h;
super.onSizeChanged(w, h, oldw, oldh);
//_thread.initialize();
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (!previouslyRunning) {
_thread = new GameThread(getHolder(), this);
_thread.initialize();
}
_thread.setRunning(true);
_thread.start();
previouslyRunning = true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//TODO - this was an Auto-generated method stub...
//TODO - research what this might be useful for
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
//Put stuff that needs destructed here.
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
// will will try again and again
//TODO: figure it out....
}
}
}
public boolean onTouchEvent(MotionEvent event) {
int numPointers = event.getPointerCount();
int ptrIdx = 0;
int touch = event.getActionMasked();
if (touch == MotionEvent.ACTION_DOWN) {
while (ptrIdx < numPointers) {
int id = event.getPointerId(ptrIdx);
float xp = event.getX(ptrIdx) / width;
if (xp > 0.6) {
_thread.shieldFront = false;
}
if (xp > 0.6 && !attacks) {
attacks = true;
_thread.attackandDefendToggle(true);
} else if (xp > 0.6 && attacks) {
attacks = false;
_thread.attackandDefendToggle(false);
} else if ((xp < 0.4 && xp > 0.2) && !movedRight) {
movedRight = true;
_thread.moveRight(true);
} else if ((xp < 0.4 && xp > 0.2) && movedRight) {
movedRight = false;
_thread.moveRight(false);
} else if (xp < 0.2 && !movedLeft) {
movedLeft = true;
_thread.moveLeft(true);
} else if (xp < 0.2 && movedLeft) {
movedLeft = false;
_thread.moveLeft(false);
}
ptrIdx++;
}
}
if (touch == MotionEvent.ACTION_UP) {
_thread.moveLeft(false);
_thread.moveRight(false);
_thread.attackandDefendToggle(false);
attacks = false;
_thread.shieldFront = true;
}
return true;
}
class GameThread extends Thread {
/****************************
* Public functions *
****************************/
public GameThread(SurfaceHolder surfaceHolder, GameView panel) {
_surfaceHolder = surfaceHolder;
_panel = panel;
// put sounds here.
soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0);
//more sounds later
//TODO: create sounds
}
/************************************************
* update() function updates all variables, *
* such as physics, Canvas draw points, score *
* life, etc.. It is called before draw. *
************************************************/
private void update() {
// all the values I want updated with each callback
}
/************************************************
* draw() function creates images on screen, *
* but it performs no logic. *
************************************************/
private void draw(Canvas canvas) {
if (canvas == null) {
return;
}
//Draw stuff on screen
}
public void initialize() {
// Values I want the program to start with;
}
public void setRunning(boolean run) {
_run = run;
}
//Code below actually runs the thread.
#Override
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
// Update the game state
update();
// Draw image
draw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}

Android: moving image on screen

What I want to achieve with this program is , when my finger is touching the image and I am moving(dragging) it on the screen , the image should move along , and as soon as I release my finger , the image should remain at the last touched position. But my image keeps moving on the screen from first touched position to last touched position as the loop keeps updating, giving me this effect.
Here is MainGamePanel class which draws image on screen:
public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback{
private static final String TAG = MainGamePanel.class.getSimpleName();
private MainThread thread;
private Droid droid;
public MainGamePanel(Context context) {
super(context);
getHolder().addCallback(this);
setFocusable(true);
thread = new MainThread(getHolder() , this);
droid = new Droid(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher), 50, 50);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while(retry)
{
try {
thread.join();
retry = false;
} catch (InterruptedException e) { }
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
droid.handleActionDown((int)event.getX(), (int)event.getY());
if(event.getY() > getHeight() - 50)
{
thread.setRunning(false);
((Activity) getContext()).finish();
}
else
{
Log.d(TAG, "Cords: x = "+event.getX()+" ,y = "+event.getY());
}
}
if(event.getAction() == MotionEvent.ACTION_MOVE)
{
if(droid.isTouched())
{
droid.setX((int)event.getX());
droid.setY((int)event.getY());
}
}
if(event.getAction() == MotionEvent.ACTION_UP)
{
if(droid.isTouched())
{
droid.setTouched(false);
}
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
droid.draw(canvas);
}
}
And here is the handleActionDown method of Droid class:
public void handleActionDown(int eventX, int eventY)
{
if(eventX >=(x - bitmap.getWidth()/2) && (eventX <= x + (bitmap.getWidth()/2)))
{
if(eventY >=(y - bitmap.getHeight()/2) && (y <= y + (bitmap.getHeight()/2)))
{
setTouched(true);
}else
{
setTouched(false);
}
}
else
{
setTouched(false);
}
}
You just need to clear the canvas before drawing the image.
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
//Just call this line
canvas.drawColor(0, Mode.CLEAR);
canvas.drawBitmap(mBitmap1,mBitmap1Point.x-(mBitmap1.getWidth()/2),mBitmap1Point.y-(mBitmap1.getHeight()/2),null);
}
Worked fine for me.

Android animation acting strange with .sleep() onTouchEvent

I currently have (among others) these classes:
public class Main extends Activity {
Panel p;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
p = new Panel(this);
setContentView(p);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
p.onTouchEvent(event);
// Make your UI thread sleep.
try {
Thread.sleep(30);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
and
public class Panel extends SurfaceView implements SurfaceHolder.Callback {
private ViewThread mThread;
private ArrayList<GraphicsElement> mElements = new ArrayList<GraphicsElement>();
public static int panelHeight;
public static int panelWidth;
private int numberOfElements = 0;
private Paint mPaint;
public Panel(Context context) {
super(context);
getHolder().addCallback(this);
mThread = new ViewThread(this);
mPaint = new Paint();
mPaint.setColor(Color.CYAN);
mPaint.setTextSize(20);
}
public void doDraw(long elapsed, Canvas canvas) {
canvas.drawColor(Color.parseColor("#003045"));
if (!(mElements.size() > 15)) {
synchronized (mElements) {
for (GraphicsElement element : mElements) {
element.doDraw(canvas);
}
canvas.drawText("FPS: " + Math.round(1000f / elapsed) + " Elements: " + numberOfElements, 10, 30, mPaint);
}
} else {
mElements.clear();
numberOfElements = 0;
}
}
public void animate(long elapsedTime) {
synchronized (mElements) {
for (GraphicsElement element : mElements) {
element.animate(elapsedTime);
}
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int xspeed = 0;
int yspeed = 0;
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
if (event.getX() > panelWidth / 2) {
xspeed = 5;
} else if (event.getX() < panelWidth / 2) {
xspeed = -5;
}
synchronized (mElements) {
for (GraphicsElement element : mElements) {
element.changeSpeed(xspeed, yspeed);
}
}
}
return true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (!mThread.isAlive()) {
mThread = new ViewThread(this);
mThread.setRunning(true);
mThread.start();
}
mElements.add(new GraphicsElement(getResources(), 80, 300));
numberOfElements += 1;
}
public void surfaceDestroyed(SurfaceHolder holder) {
I also have ViewThread, just my animation thread, and GraphicsElement, which defines the moving object. My animation is going very slow(on touch), and I think it has something to do with my .sleep() method. Could anyone please help me ?
Edit: I'm using .sleep() because I don't want to flood TouchEvents.
i'm trying to get it like: Check for TouchEvent, Sleep, Check for TouchEvent, Sleep.. etc...
I've had the same issues with touch slowing things down and ended up with a similar approach to yours (sleeping the UI thread on touch events, as recommended by a Google game developer). The thing that seemed to help me was playing with the length of the sleep time. Try increasing it to 70 ms or even more and see if that helps. Most apps don't need a super high sample rate for touch input.
Firstly, you should initialize your Panel inside your onCreate() method.
Panel p = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
p = new Panel(this);
setContentView(p);
}
Secondly, I do not understand why you are doing a sleep in your onTouchEvent() handler. Try taking that out, and things should speed up, especially if you think that may be the cause!

Animation in Live Wallpaper Android?

I am fairly new developer and trying to make a live wallpaper app. Among many animations, my first target is to show a rotating Bitmap which is actually a Black Hole.
public class Painting extends Thread {
/** Reference to the View and the context */
private SurfaceHolder surfaceHolder;
private Context context;
/** State */
private boolean wait;
private boolean run;
/** Dimensions */
private int width;
private int height;
/** Time tracking */
private long previousTime;
boolean first = true;
Bitmap hole;
int degree;
public Painting(Context con , SurfaceHolder surf)
{
context = con;
surfaceHolder = surf;
this.wait = true;
Log.i("Live Test","UnInitialized");
Drawable d = (con.getResources().getDrawable(R.drawable.vlack));
hole = ((BitmapDrawable)d).getBitmap();
hole.prepareToDraw();
if(hole != null)
Log.i("Live Test","Initialized");
run = true;wait = false;
degree = 0;
}
#Override
public void run()
{
while (run) {
this.run = true;
Canvas c = null;
Log.i("Live Test","Draw Color");
while (run) {
try {
c = this.surfaceHolder.lockCanvas();
synchronized (this.surfaceHolder) {
doDraw(c);
}
} finally {
if (c != null) {
this.surfaceHolder.unlockCanvasAndPost(c);
Log.i("Live Test","Unlocked And Posted");
}
}
// pause if no need to animate
synchronized (this) {
if (wait) {
try {
wait();
} catch (Exception e) {
Log.i("Live Test","Error wait");
}
}
}
}
}
}
public void setSurfaceSize(int width, int height) {
this.width = width;
this.height = height;
synchronized(this) {
this.notify();
}
}
/**
* Pauses the livewallpaper animation
*/
public void pausePainting() {
this.wait = true;
synchronized(this) {
this.notify();
}
}
/**
* Resume the livewallpaper animation
*/
public void resumePainting() {
this.wait = false;
synchronized(this) {
this.notify();
}
}
/**
* Stop the livewallpaper animation
*/
public void stopPainting() {
this.run = false;
synchronized(this) {
this.notify();
}
}
private void doDraw(Canvas canvas) {
if(first)
{
canvas.save();
canvas.drawColor(0x60444444);
canvas.drawBitmap(hole, 80,80,null);
canvas.restore();
first = false;
}
else
{
long currentTime = System.currentTimeMillis();
long elapsed = currentTime - previousTime;
if (elapsed > 20) {
canvas.save();
degree+= 5;
if(degree>359)degree = degree -358;
canvas.rotate((float) degree);
canvas.restore();
Log.i("Live Test","rotated");
}
previousTime = currentTime;
}
}
}
So I am trying to rotate the bitmap and show it again so it looks like its sucking stars and all.
Also I have removed Basic onPause onResume Functions so that you guys can understand the code easily. I know there is something basic I am missing, but What?
Hmmm. It would take more time than I have right now to puzzle this out, but I do have one suggestion: code your wallpaper using a simpler approach. Scrap the thread and do something more along the lines of the Cube example, that is to say make a runnable which schedules calls to itself using postDelayed. Hope this helps. George.

Categories

Resources