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.
Related
My app is a little jump and run game. The moment you die, the activity sleeps for 1 sec. If I touch in that time the screen (so that the OnTouchEvent gets triggered) I get the error "unfortunately app has stopped".
Is there a way to ignore touch events?
public class GamePanel extends SurfaceView implements SurfaceHolder.Callback{
private MainThread thread;
private Background bg;
private Player player;
MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.background_sound);
MediaPlayer copter = MediaPlayer.create(getContext(), R.raw.helicoptersound);
public GamePanel(Context context) {
super(context);
getHolder().addCallback(this);
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
setWillNotDraw(false);
bg = new Background(BitmapFactory.decodeResource(getResources(), R.raw.grassbg1), 900);
player = new Player(BitmapFactory.decodeResource(getResources(), R.raw.helicopter), 132, 47, 3);
thread = new MainThread(getHolder(), this);
// start game loop
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
mp.release();
copter.release();
while (true) {
try {
thread.setRunning(false);
thread.join();
thread = null;
}
catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (((Activity)getContext()) != null) {
if (event.getAction() == MotionEvent.ACTION_DOWN && fuel > 0) {
if (!player.getPlaying()) {
player.setPlaying(true);
player.setUp(true);
}
else {
player.setUp(true);
}
return true;
}
if (event.getAction() == MotionEvent.ACTION_UP) {
copter.pause();
player.setUp(false);
return true;
}
}
return super.onTouchEvent(event);
}
public void update() {
postInvalidate();
if (player.getPlaying()) {
bg.update();
player.update();
// check if player out of range
if (player.getY() > HEIGHT || player.getY() < 0) {
player.setPlaying(false);
newGame(score);
}
}
}
public boolean collision(GameObject a, GameObject b) {
if (Rect.intersects(a.getRectangle(), b.getRectangle())) {
return true;
}
return false;
}
#Override
public void draw(Canvas canvas) {
if (canvas != null) {
bg.draw(canvas);
player.draw(canvas);
}
}
public void newGame(int score) {
mp.release();
copter.release();
try {
thread.setRunning(false);
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
Bundle basket13 = new Bundle();
basket13.putInt("key2", score);
Context context = getContext();
Intent intent = new Intent(context, MainActivity.class);
intent.putExtras(basket13);
context.startActivity(intent);
((Activity)context).finish(); // letztes was ausgeführt wird
}
}
In My application, I want to make one countdown Screen for remaining days (like Countdown the days till Christmas) and I want to set that remaining days (With some animation) in live wallpaper screen as shown in the image.
And I need to play some music when click or double tap in the wallpaper.
I am creating GifLiveWallPaper class for set my custom live wallpaper.
GifLiveWallPaper
public class GifLiveWallPaper extends WallpaperService {
static final String TAG = "LIVE_WALLPAPER";
static final Handler liveHandler = new Handler();
#Override
public Engine onCreateEngine() {
try {
return new WallPaperEngine();
} catch (IOException e) {
Log.w(TAG, "Error creating WallPaperEngine", e);
stopSelf();
return null;
}
}
class WallPaperEngine extends Engine {
private Movie liveMovie;
private int duration;
private Runnable runnable;
float mScaleX;
float mScaleY;
int mWhen;
long mStart;
public WallPaperEngine() throws IOException {
InputStream is = getResources().openRawResource(R.raw.my_gif_image);
if (is != null) {
try {
liveMovie = Movie.decodeStream(is);
duration = liveMovie.duration();
} finally {
is.close();
}
} else {
throw new IOException("Unable to open R.raw.hand");
}
mWhen = -1;
runnable = new Runnable() {
public void run() {
nyan();
}
};
}
#Override
public void onDestroy() {
super.onDestroy();
liveHandler.removeCallbacks(runnable);
}
#Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (visible) {
nyan();
} else {
liveHandler.removeCallbacks(runnable);
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
mScaleX = width / (1f * liveMovie.width());
mScaleY = height / (1f * liveMovie.height());
nyan();
}
#Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xOffsetStep, float yOffsetStep, int xPixelOffset,
int yPixelOffset) {
super.onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep,
xPixelOffset, yPixelOffset);
nyan();
}
void nyan() {
tick();
SurfaceHolder surfaceHolder = getSurfaceHolder();
Canvas canvas = null;
try {
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
drawGif(canvas);
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
liveHandler.removeCallbacks(runnable);
if (isVisible()) {
liveHandler.postDelayed(runnable, 1000L / 25L);
}
}
void tick() {
if (mWhen == -1L) {
mWhen = 0;
mStart = SystemClock.uptimeMillis();
} else {
long mDiff = SystemClock.uptimeMillis() - mStart;
mWhen = (int) (mDiff % duration);
}
}
void drawGif(Canvas canvas) {
canvas.save();
canvas.scale(mScaleX, mScaleY);
liveMovie.setTime(mWhen);
liveMovie.draw(canvas, 0, 0);
canvas.restore();
}
}
}
How can I set remaining days and music as described in above ?
To play some music when click or double tap in the live wallpaper, add the following code to GifLiveWallPaper class. (Place your_music_file in res--> raw folder)
MediaPlayer mPlayer=new MediaPlayer();
#Override
public void onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
mDoubleTapDetector.onTouchEvent(event);
}
private GestureDetector mDoubleTapDetector = new GestureDetector(GifLiveWallPaper.this,
new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onDoubleTap(MotionEvent e) {
if(!mPlayer.isPlaying())
{
mPlayer= MediaPlayer.create(GifLiveWallPaper.this, R.raw.your_music_file);
mPlayer.start();
}
else
{
mPlayer.stop();
}
return true;
}
});
#Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (!visible) {
if(mPlayer.isPlaying())
{
mPlayer.stop();
}
}
}
#Override
public void onDestroy() {
super.onDestroy();
liveHandler.removeCallbacks(runnable);
if(mPlayer.isPlaying())
{
mPlayer.stop();
}
}
I try to display one from list of bitmaps during onDraw.
When i'm passing list to the canvas all are display and stay in their places.
When I pass one random bitmaps it's redrawing canvas all the time.
All works when i'm using public void drawEnemy(Canvas canvas) but not exactly like I want when using public void drawEn(Canvas canvas).
I want to display one random bitmap, then after a few seconds, delete it and display other bitmap. I think the problem is how I implemented onDrow() method. It's redrawing canvas all the time.
Activity:
public class NewGameActivity extends Activity{
NewGame newgame;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// Landscape mode
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// no title
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
// content Newgame.java
newgame = new NewGame(this);
setContentView(newgame);
}
Thread:
public class MainThread extends Thread{
private SurfaceHolder surfaceHolder;
private NewGame screen;
public MainThread(SurfaceHolder surfaceHolder, NewGame ekran) {
super();
this.surfaceHolder = surfaceHolder;
this.screen= screen;
}
private boolean running;
public void setRunning(boolean running) {
this.running = running;
}
#Override
public void run() {
Canvas canvas;
while (running) {
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
this.screen.onDraw(canvas);
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
SurfaceView:
public class NewGame extends SurfaceView implements SurfaceHolder.Callback{
private MainThread thread;
private EnemyManager manager;
public NewGame(Context context) {
super(context);
getHolder().addCallback(this);
thread = new MainThread(getHolder(), this);
manager = new EnemyManager();
// TODO Auto-generated constructor stub
//adding enemy
Enemy e1 = new Enemy(BitmapFactory.decodeResource(getResources(), R.drawable.card), 1);
Enemy e2 = new Enemy(BitmapFactory.decodeResource(getResources(), R.drawable.horse), 2);
EnemyLocation l1 = new EnemyLocation(60, 180);
EnemyLocation l2 = new EnemyLocation(60, 50);
manager.AddEnemy(e1, l1);
manager.AddEnemy(e2, l2);
setFocusable(true);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.saloon), 0, 0, null);
manager.drawEn(canvas);
}
#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) {
thread.setRunning(false);
thread.stop();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
manager.handleActionDown((int)event.getX(), (int)event.getY());
}
return true;
}
}
EnemyManager:
public class EnemyManager {
private ArrayList<Enemy> enemyList;
private ArrayList<Enemy> suspects;
private Enemy cow;
private String message;
private int suspectID;
private Random rnd;
public String getMessage() {
return message;
}
public EnemyManager(){
enemyList = new ArrayList<Enemy>();
suspects = new ArrayList<Enemy>();
}
public void AddEnemy(Enemy enemy, EnemyLocation loc){
// set x,y enemy localization
enemy.setX(loc.getX());
enemy.setY(loc.getY());
enemyList.add(enemy);
}
public void clearEnemy() {
enemyList.clear();
}
// message if enemy touched
public void handleActionDown(int x, int y) {
for (Enemy enemy: enemyList) {
if (enemy.wasTouched(x, y)) {
message = enemy.getId();
return;
}
}
}
public void PrepareEnemy(){
suspectID = enemyList.get(rnd.nextInt(enemyList.size()+1)).getId();
suspects = new ArrayList<Enemy>();
suspects.add(getSuspectByID(suspectID));
}
private Enemy SingleEnemy(){
Double i = 1 + Math.random() * ((enemyList.size()-1)+1);
cow = getSuspectByID(i.intValue());
return cow;
}
private Enemy getSuspectByID(int suspectID) {
for (Enemy s: enemyList) {
if (s.getId() == suspectID) {
return s;
}
}
return null;
}
public void drawEn(Canvas canvas){
try {
Enemy k = SingleEnemy();
canvas.drawBitmap(cow.picture, cow.x, cow.y, null);
} catch (Exception e) {
// TODO: handle exception
}
}
// draw enemy
public void drawEnemy(Canvas canvas) {
try {
for (Enemy enemy: enemyList) {
canvas.drawBitmap(enemy.picture, enemy.x, enemy.y, null);
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
das
As for as understand you are trying to do something like this (if it's not, please correct me):
This is rendering the canvas with all components:
Draw background
Draw enemy
To "refresh" the canvas you simply do something like this:
Draw background
Update
To pause the rendering you could do something like this:
int lastUpdateTime;
int delayTime = 2000; 2 seconds
if(System.currenttimeMillis() > lastUpdateTime + delayTime) {
// Finished waiting
}
You should only define lastUpdateTime when you want to wait and not in every iteration.
NB: Don't call Thread.sleep() in a rendering thread!
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 :)
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!