Handling TouchEvents concurrentModificationException android - java

I was trying to draw some balloons on screeen using canvas in SurfaceView class, I was successfully able to draw Multiple balloons on canvas and animate them by swapping different images. The problem is When I try touching on Oneballoon I need to remove it from screen .Here I get this exception and I am stuck
Code:
MainActivity:
package com.pradhul.game.touchball;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new GameView(this));
/*TODO Hide the bottom navigation bar */
}
}
GameView.java
package com.pradhul.game.touchball;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GameView extends SurfaceView implements SurfaceHolder.Callback, View.OnTouchListener {
private static final int NUM_OF_BALLOONS = 5; //TODO for more than one balloons animations dont work(?)
/*use SurfaceView because we want complete control over the screen.
* unlike extending View class the oDraw() Method will not be called automatically
* from the method onSurfaceCreated() we have to call it Manually and pass a canvas object into it
* */
private final SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private List<Balloon> balloons = new ArrayList<>();
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
holder = getHolder();
holder.addCallback(this);
createBalloons(NUM_OF_BALLOONS);
this.setOnTouchListener(this);
}
private void createBalloons(int count) {
for(int i=0 ; i< count ;i++){
balloons.add(createBalloon());
}
}
#Override
protected void onDraw(Canvas canvas) {
if(canvas != null) {
canvas.drawColor(Color.WHITE);
for(Balloon balloon : balloons){
try {
gameLoopThread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
balloon.onDraw(canvas);
}
}
}
#SuppressLint("WrongCall")
#Override
public void surfaceCreated(SurfaceHolder holder) {
/*this is called when the view is created*/
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
/*pausing game Thread*/
gameLoopThread.setRunning(false);
while (true){
try {
gameLoopThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Balloon createBalloon(){
return new Balloon(this);
}
#Override
public synchronized boolean onTouch(View v, MotionEvent event) {
Log.d("OnTouch real -", "x: " + event.getX() + ", Y: " + event.getY());
/* for (int i = balloons.size()-1; i >= 0; i--) {
Balloon balloon = balloons.get(i);
Log.d("OnTouch collision -", !balloon.isCollision(event.getX(), event.getY())+"");
if (!balloon.isCollision(event.getX(), event.getY())) {
balloons.remove(0);
break;
}
}*/
Iterator<Balloon> balloonIterator = balloons.iterator();
while(balloonIterator.hasNext()){
Balloon balloon = balloonIterator.next();
balloons.remove(0);
}
return true;
}
}
GameLoopThread.java
package com.pradhul.game.touchball;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
public class GameLoopThread extends Thread {
private GameView view;
private boolean running = false;
public GameLoopThread(GameView view){
this.view = view;
}
public void setRunning(boolean run){
running = run;
}
#SuppressLint("WrongCall")
public void run(){
while (running){
Canvas canvas = null;
try{
canvas = view.getHolder().lockCanvas();
synchronized (view.getHolder()){
view.onDraw(canvas);
}
}finally{
if(canvas != null) {
view.getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
}
Balloon.java
package com.pradhul.game.touchball;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.Log;
import java.util.Random;
public class Balloon {
private static final int BALLOON_SPEED = 10;
private int y = 0;
private int x = 0;
private int speed = 1;
private GameView gameView;
private Bitmap balloon;
public Bitmap[] normalBalloons;
private int balloonIndex = 0;
private int normalImages[] = {R.drawable.normal_01,R.drawable.normal_02,R.drawable.normal_03,
R.drawable.normal_04,R.drawable.normal_05,R.drawable.normal_06,R.drawable.normal_07,
R.drawable.normal_08,
};
private int crackingImages[] = {R.drawable.crack_01,R.drawable.crack_02,R.drawable.crack_03,
R.drawable.crack_04, R.drawable.crack_05,R.drawable.crack_04,R.drawable.crack_03,
R.drawable.crack_02
};
private boolean reverseSwap = false;
public Balloon(GameView gameView){
this.gameView = gameView;
normalBalloons = new Bitmap[8];
setUpImages();
}
public void onDraw(Canvas canvas){
/*draws the balloon in canvas */
animateBalloon();
update(canvas.getWidth());
canvas.drawBitmap(balloon, x, y, null);
}
public boolean isCollision(float x2, float y2) {
return x2 > x && x2 < x + balloon.getWidth() && y2 > y && y2 < y + balloon.getHeight();
}
private int getRandomX(int maxVal) {
Random rand = new Random();
return rand.nextInt(maxVal);
}
private void animateBalloon() {
/*Animates the balloon by swapping resource image at each call*/
this.balloon = getBalloons();
Log.d("Balloon",balloonIndex % normalBalloons.length + "");
}
private void update(int canvasWidth) {
/*updates the y position for moving the balloon*/
if (y <= 0){
/*so that the balloon starts from bottom
* gameView will return a height only after the View is ready
* getting 0 in constructor of this class*/
y = gameView.getHeight();
/*x is assigned a random between the width od the canvas
* so that the balloons will appear random positions from below*/
x = getRandomX(canvasWidth - balloon.getWidth());
}
if (y > gameView.getHeight() - balloon.getHeight() - speed) {
speed = -BALLOON_SPEED;
}
y = y + speed;
Log.d("Balloon","Positions:"+x+","+y);
}
private Bitmap getBalloons() {
if(balloonIndex == normalBalloons.length-1) {
reverseSwap = true;
}
if(balloonIndex == 0){
reverseSwap = false;
}
balloonIndex = reverseSwap?balloonIndex-1:balloonIndex+1;
return normalBalloons[balloonIndex];
}
private void setUpImages() {
/*setting up resources array*/
for(int count =0; count < normalImages.length; count++){
Bitmap balloon = BitmapFactory.decodeResource(gameView.getResources(), normalImages[count]);
normalBalloons[count] = balloon;
}
}
}
I am confused that why it causes an error like this , Can anybody can take look at it and suggest me a solution, is this the right way to remove ?
please share any suggestion
Thanks

It's the wrong way to remove.
If you iterate over a Collection / List, and want to remove the current element, you must use Iterator.remove() method.
In your case, simply call balloonIterator.remove() instead of balloons.remove(0)
Even simpler, since you want to remove all elements from the list - you should simply call balloons.clear() and remove the loop completely.

This exception is due to concurrent modification of list balloons.
As soon as you touch surface view onDraw() gets invoked with onTouch(), in which you are working on list balloons.
I think you should add touch listener to balloon instead of GameView.

okay ,
I need to wrap all my code inside a synchronized holder inorder this to work
(don't know much about it)
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("OnTouch","x:"+event.getX()+"Y:"+event.getY());
synchronized (getHolder()){
for (int i=0 ;i<balloons.size();i++){
balloons.remove(0);
break;
}
}
return true;
}

Related

Android game loop lag increases drastically over time

I am making this simple 2D canvas game using SurfaceView for android and am having this problem with my game loop: the fps immensely lowers as time goes on. I found the design for the game loop from this article: deWiTTERS Game Loop.
GamePanel.java
package jacobmahoney.guardianofearth;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
public class GamePanel extends SurfaceView implements SurfaceHolder.Callback {
private MainThread thread;
private SpaceshipObject spaceship;
private LeftCircle leftCircle;
private RightCircle rightCircle;
private int screenWidth;
private int screenHeight;
public GamePanel(Context context) {
super(context);
getHolder().addCallback(this);
thread = new MainThread(getHolder(), this);
setFocusable(true);
getScreenSize(context);
spaceship = new SpaceshipObject(screenWidth, screenHeight);
leftCircle = new LeftCircle(screenWidth, screenHeight);
rightCircle = new RightCircle(screenWidth, screenHeight);
}
public void getScreenSize(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getRealSize(size);
screenWidth = size.x;
screenHeight = size.y;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread = new MainThread(getHolder(), this);
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while (retry) {
try {
thread.setRunning(false);
thread.join();
} catch(Exception e) {
e.printStackTrace();
}
retry = false;
}
}
public void update() {
rightCircle.update();
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawColor(Color.BLUE);
spaceship.draw(canvas);
leftCircle.draw(canvas);
rightCircle.draw(canvas);
}
}
MainThread.java
package jacobmahoney.guardianofearth;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
public class MainThread extends Thread {
private SurfaceHolder surfaceHolder;
private GamePanel gamePanel;
private boolean running;
public static Canvas canvas;
public void setRunning(boolean running) {
this.running = running;
}
public MainThread(SurfaceHolder surfaceHolder, GamePanel gamePanel) {
super();
this.surfaceHolder = surfaceHolder;
this.gamePanel = gamePanel;
}
#Override
public void run() {
int TICKS_PER_SECOND = 50;
int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
int MAX_FRAMESKIP = 10;
long next_game_tick = System.currentTimeMillis();
int loops;
long prev = System.currentTimeMillis();
while (running) {
Log.d("MainThread", "" + (System.currentTimeMillis() - prev)); // this value drastically increases over time
prev = System.currentTimeMillis();
loops = 0;
while (System.currentTimeMillis() > next_game_tick && loops < MAX_FRAMESKIP) {
this.gamePanel.update();
next_game_tick += SKIP_TICKS;
loops++;
}
canvas = this.surfaceHolder.lockCanvas();
this.gamePanel.draw(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
Any help would be greatly appreciated, thanks!
EDIT:
After commenting out all the instances of the game objects: leftCircle, rightCircle, and spaceship completely from GamePanel.java, I didn't have the issue where the game gets increasingly laggy over time, which I guess is obvious. Then when I put just the spaceship game object back into the mix, the game still got increasingly laggy over time, but at a smaller rate than it was when I had all three game objects drawing to the canvas.
I am completely stumped, I have literally no idea what could be causing this... any ideas?
EDIT2:
removed
EDIT3:
Found out what the issue was. It was a problem with the code I was using to draw paths for each object. I forgot to place a path.reset() and path.close() before and after the code I used to draw the path. Also I removed my edit2 because I made a comment in the heat of my frustration, that was unwarranted.

generating random circles in android

Im trying to make a simple app that draws random circles when one button is pushed, and clears them all from the screen when another is pushed. Currently When i start the app on an emulator for some reason it starts with an automatically generated circle, then If I attempt to push the button to generate another circle the app crashes and I get a NullPointerException at my
c.drawcircle(b.getX(), b.getY(), b.getR(), p)
method.
Heres what I currently have:
Main:
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
public class Bubbles extends Activity implements OnClickListener
{
Button b1,b2;
drawView dv;
LinearLayout frame;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bubbles);
dv = new drawView(getApplicationContext());
frame = (LinearLayout)findViewById(R.id.LinearLayout2);
b1 = (Button)findViewById(R.id.BubbleButton);
b1.setOnClickListener(this);
b2 = (Button)findViewById(R.id.ClearButton);
b2.setOnClickListener(this);
frame.addView(dv);
}
#Override
public void onClick(View v)
{
if(v==b1)
{
dv.onDraw(null);
}
if(v==b2)
{
dv.clear();
}
v.postInvalidate();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.bubbles, menu);
return true;
}
}
drawView:
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
public class drawView extends View
{
ArrayList<Bubble> bList = new ArrayList<Bubble>();
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
public drawView(Context con)
{
super(con);
}
public void onDraw(Canvas c)
{
Bubble b = new Bubble();
p.setColor(b.getColor());
p.setStyle(Paint.Style.FILL);
bList.add(b);
c.drawCircle(b.getX(), b.getY(), b.getR(), p);
}
public void clear()
{
bList.clear();
}
}
Im using an arrayList in this class to store all the bubbles I make and then clear the arrayList using the clear button.
Here is the Bubble class I made that goes with it:
Bubble:
import android.graphics.Color;
public class Bubble
{
int color;
int y;
int x;
int r;
public Bubble ()
{
color = Color.rgb(rand(0,255), rand(0,255), rand(0,255));
r = rand(0,255);
x =rand(0,255);
y = rand(0,255);
}
public int getColor() {
return color;
}
public int getY() {
return y;
}
public int getX() {
return x;
}
public int getR() {
return r;
}
/******************************rand()*****************************/
public int rand(int a, int b)
{
return((int)((b-a+1)*Math.random() + a));
}
}
Thanks for any input, This is my first time messing with graphics so Im not 100% sure why this is happening.
you pass null to your method onDraw here:
dv.onDraw(null);
I guess you would want to pass a Canvas there.
In this method then your Canvas will point to null since you passed null as the param:
public void onDraw(Canvas c) {
Bubble b = new Bubble();
p.setColor(b.getColor());
p.setStyle(Paint.Style.FILL);
bList.add(b);
c.drawCircle(b.getX(), b.getY(), b.getR(), p); // c == null here!
}
==> NPE
as for your random method try this
Random rng = new Random();
in your field declarations.
then use. rng.nextInt(255);
color = Color.rgb(rng.nextInt(255), rng.nextInt(255), rng.nextInt(255));
as for your circles: firstly you need an update method(if you want any movement)
secondly you should be using a surfaceView to draw, and thirdly you should use a
copyOnWriteArrayList, because your button may be modifying the list while its iterating over it, and copyonwritearraylist is perfect for that. here: my gift to you.
CLASS 1
import java.util.Random;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
public class GrowCircle {
float x, y;int radius;
Paint myp = new Paint();
int colr,colg,colb;
int redvar=1;
int bluevar=5;
int greenvar=2;
int tripper=10;
int change=2;
Random rand = new Random();
public GrowCircle(float x, float y){
this.x=x;
this.y=y;
this.radius=2;
this.colr=rand.nextInt(254)+1;
this.colg=rand.nextInt(254)+1;
this.colb=rand.nextInt(254)+1;
}
public void update(){
radius+=4;
tripper+=change;
if(tripper<=1||tripper>=15){
change=-change;
}
Random col = new Random();
myp.setColor(Color.argb(255,colr,colg,colb));
colr+=redvar;
colg+=greenvar;
colb+=bluevar;
if(colr<=5||colr>=250){
redvar=-redvar;
}
if(colg<=5||colg>=250){
greenvar=-greenvar;
}
if(colb<=5||colb>=250){
bluevar=-bluevar;
}
}
public void drawThis(Canvas canvas){
myp.setStrokeWidth(tripper);
myp.setStyle(Style.STROKE);
canvas.drawCircle(x, y, radius, myp);
}
}
CLASS2
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import com.gmaninc.acidrain2.R;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.service.wallpaper.WallpaperService;
import android.service.wallpaper.WallpaperService.Engine;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.View.OnTouchListener;
public class LiveWallpaperService extends WallpaperService {
CopyOnWriteArrayList<GrowCircle> gc = new CopyOnWriteArrayList<GrowCircle>();
private final Handler mHandler = new Handler();
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public Engine onCreateEngine() {
return new CubeEngine();
}
class CubeEngine extends Engine {
private final Paint mPaint = new Paint();
private float mOffset;
private float mTouchX = -1;
private float mTouchY = -1;
private long mStartTime;
private float mCenterX;
private float mCenterY;
private final Runnable mDrawCube = new Runnable() {
public void run() {
drawFrame();
}
};
private boolean mVisible;
CubeEngine() {
// Create a Paint to draw the lines for our cube
final Paint paint = mPaint;
paint.setColor(0xffffffff);
paint.setAntiAlias(true);
paint.setStrokeWidth(2);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.STROKE);
mStartTime = SystemClock.elapsedRealtime();
}
#Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
// By default we don't get touch events, so enable them.
setTouchEventsEnabled(true);
}
#Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mDrawCube);
}
#Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
drawFrame();
} else {
mHandler.removeCallbacks(mDrawCube);
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
// store the center of the surface, so we can draw the cube in the right spot
mCenterX = width/2.0f;
mCenterY = height/2.0f;
for(GrowCircle circ:gc){
circ.update();
}
drawFrame();
}
#Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
mVisible = false;
mHandler.removeCallbacks(mDrawCube);
}
#Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xStep, float yStep, int xPixels, int yPixels) {
mOffset = xOffset;
drawFrame();
}
/*
* Store the position of the touch event so we can use it for drawing later
*/
#Override
public void onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mTouchX = event.getX();
mTouchY = event.getY();
} else {
mTouchX = -1;
mTouchY = -1;
}
float tx = event.getX();
float ty= event.getY();
gc.add(new GrowCircle(tx,ty));
super.onTouchEvent(event);
}
/*
* Draw one frame of the animation. This method gets called repeatedly
* by posting a delayed Runnable. You can do any drawing you want in
* here. This example draws a wireframe cube.
*/
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
// draw something
drawCircs(c);
for(GrowCircle circ:gc){
if(circ.radius>350){
gc.remove(circ);
}
circ.update();
circ.drawThis(c);
}
drawTouchPoint(c);
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
// Reschedule the next redraw
mHandler.removeCallbacks(mDrawCube);
if (mVisible) {
mHandler.postDelayed(mDrawCube, 1000 / 25);
}
}
/*
* Draw a wireframe cube by drawing 12 3 dimensional lines between
* adjacent corners of the cube
*/
void drawCircs(Canvas c) {
c.save();
c.translate(mCenterX, mCenterY);
c.drawColor(0xff000000);
c.restore();
}
/*
* Draw a circle around the current touch point, if any.
*/
void drawTouchPoint(Canvas c) {
if (mTouchX >=0 && mTouchY >= 0) {
c.drawCircle(mTouchX, mTouchY, 80, mPaint);
}
}
}
}
MANIFEST
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gmaninc.acidrain2"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="16" />
<uses-feature
android:name="android.software.live_wallpaper"
android:required="true" >
</uses-feature>
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<service
android:name="LiveWallpaperService"
android:enabled="true"
android:label="Acid Rain 2"
android:permission="android.permission.BIND_WALLPAPER" >
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" >
</action>
</intent-filter>
<meta-data
android:name="android.service.wallpaper"
android:resource="#xml/mywallpaper" >
</meta-data>
</service>
</application>
</manifest>
this is a livewallpaper that creates circles onTouch... feel free to play around with it, you should find what you need in here....
NOTE: you may need to change the package name in the manifest to get it to run!

want to count time in activity when it starts

a small time based question > I want to get timer in activity I already tryed this code but it runs so fast I offen get " 0 " I want to program to run with no sleep . but to count from 0 to 60 sec
package com.okok;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Handler;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
import android.widget.Chronometer;
class GameView extends SurfaceView {
//private Bitmap bmp;
// Chronometer mChronometer;
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private List<Sprite> sprites = new ArrayList<Sprite>();
private long lastClick;
private Bitmap bmpBlood;
Handler m_handler;
Runnable m_handlerTask ;
private List<TempSprite> temps = new ArrayList<TempSprite>();
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
holder = getHolder();
holder.addCallback(new Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
createSprites();
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
bmpBlood = BitmapFactory.decodeResource(getResources(), R.drawable.blast2);
}
private void createSprites() {
sprites.add(createSprite(R.drawable.greenenact));
// sprites.add(createSprite(R.drawable.greenenact));
// sprites.add(createSprite(R.drawable.greenenact));
}
private Sprite createSprite(int resouce) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), resouce);
return new Sprite(this, bmp);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.rgb(21, 181, 195));
for (int i = temps.size() - 1; i >= 0; i--) {
temps.get(i).onDraw(canvas);
}
final int count=0;
m_handler = new Handler();
m_handlerTask = new Runnable()
{
#Override
public void run() {
// do something
if(count=60)
{
m_handler.removeCallbacks(m_handlerTask);
}
count++;
m_handler.postDelayed(m_handlerTask, 5000);
for (Sprite sprite : sprites) {
sprite.onDraw(canvas);
}
}
};
m_handlerTask.run();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (System.currentTimeMillis() - lastClick > 500) {
lastClick = System.currentTimeMillis();
synchronized (getHolder()) {
float x = event.getX();
float y =event.getY();
for (int i = sprites.size() - 1; i >= 0; i--) {
Sprite sprite = sprites.get(i);
if (sprite.isCollition(x, y)) {
sprites.remove(sprite);
temps.add(new TempSprite(temps, this, x, y, bmpBlood));
break;
}
}
}
}
return true;
}
}
this is my program I have only one error
can not change count from int to a boolean error
I don't want to use sleep method because my application have sleep method that affect my animation
or give me a program that counts 0 to 60 sec and displays which runs without a sleep method
Use chronometer instead. Here is example ,
Example 2
Use a Handler
Handler m_handler;
Runnable m_handlerTask ;
int count=0;
m_handler = new Handler();
m_handlerTask = new Runnable()
{
#Override
public void run() {
// do something
if(count=60)
{
m_handler.removeCallbacks(m_handlerTask);
}
count++:
m_handler.postDelayed(m_handlerTask, 1000);
// repeat some task every 1 second
}
};
m_handlerTask.run();

Dragging images after touch cordinates in android

I am making an android game that should drag an image after my touch cordinates in onTouchEvent(MotionEvent event) method. So far i have managed to move it by clicking, but not by dragging, how can i do this? Please help me and thanks so much in advance! Check out my onTouchEvent(MotionEvent event) method in this code:
package com.mysoftwaremobileapps.TriangleUnlocker;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.media.MediaPlayer;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.TextView;
import android.widget.Toast;
public class GameScreenActivity extends SurfaceView implements SurfaceHolder.Callback
{
class ExampleThread extends Thread
{
private ArrayList<Triangle> triangles;
private Bitmap TriangleImage;
private Paint black;
private boolean running;
private SurfaceHolder mSurfaceHolder;
private Context mContext;
private Context mContext1;
private Handler mHandler;
private Handler mHandler1;
private TriangleUnlockerActivity mActivity;
private long frameRate;
private boolean loading;
public float x;
public float y;
public float x1;
public float y1;
public MediaPlayer mp1;
public int parachuterIndexToResetAndDelete;
public int canvasGetWidth;
public int livesLeftValue;
public ExampleThread(SurfaceHolder sHolder, Context context, Handler handler)
{
mSurfaceHolder = sHolder;
mHandler = handler;
mHandler1 = handler;
mContext = context;
mActivity = (TriangleUnlockerActivity) context;
triangles = new ArrayList<Triangle>();
TriangleImage = BitmapFactory.decodeResource(getResources(), R.drawable.triangle);
black = new Paint();
black.setStyle(Paint.Style.FILL);
black.setColor(Color.GRAY);
running = true;
// This equates to 26 frames per second.
frameRate = (long) (1000 / 26);
loading = true;
}
#Override
public void run()
{
while (running)
{
Canvas c = null;
try
{
c = mSurfaceHolder.lockCanvas();
synchronized (mSurfaceHolder)
{
long start = System.currentTimeMillis();
doDraw(c);
long diff = System.currentTimeMillis() - start;
if (diff < frameRate)
Thread.sleep(frameRate - diff);
}
} catch (InterruptedException e)
{
}
finally
{
if (c != null)
{
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
protected void doDraw(Canvas canvas)
{
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), black);
canvasGetWidth = canvas.getWidth();
//Draw
for (int i = 0; i < triangles.size(); i++)
{
canvas.drawBitmap(TriangleImage, triangles.get(i).getX(), triangles.get(i).getY(), null);
}
}
public boolean onTouchEvent(MotionEvent event)
{
if (event.getAction() != MotionEvent.ACTION_DOWN)
Toast.makeText(getContext(), "onTouchEvent invoked. X= " + event.getX() + " Y= " + event.getY(), 15).show();
x1 = event.getX();
y1 = event.getY();
for (Triangle p: triangles) {
Toast.makeText(getContext(), "Moving the triangle after touch cordinates", 25).show();
p.posX = event.getX();
p.posY = event.getY();
return false;
}
return loading;
}
public void displayTriangles()
{
//Parachuter nr.1
x = 14;
y = 28;
Triangle p = new Triangle(x, y);
triangles.add(p);
}
public void setRunning(boolean bRun)
{
running = bRun;
}
public boolean getRunning()
{
return running;
}
}
/** Handle to the application context, used to e.g. fetch Drawables. */
private Context mContext;
/** Pointer to the text view to display "Paused.." etc. */
private TextView mStatusText;
/** The thread that actually draws the animation */
private ExampleThread eThread;
public GameScreenActivity(Context context)
{
super(context);
// register our interest in hearing about changes to our surface
SurfaceHolder holder = getHolder();
holder.addCallback(this);
// create thread only; it's started in surfaceCreated()
eThread = new ExampleThread(holder, context, new Handler()
{
#Override
public void handleMessage(Message m)
{
// mStatusText.setVisibility(m.getData().getInt("viz"));
// mStatusText.setText(m.getData().getString("text"));
}
});
setFocusable(true);
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
return eThread.onTouchEvent(event);
}
public ExampleThread getThread()
{
return eThread;
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3)
{
// TODO Auto-generated method stub
}
public void surfaceCreated(SurfaceHolder holder)
{
if (eThread.getState() == Thread.State.TERMINATED)
{
eThread = new ExampleThread(getHolder(), getContext(), getHandler());
eThread.start();
}
else
{
eThread.start();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder)
{
boolean retry = true;
eThread.setRunning(false);
while (retry)
{
try
{
eThread.join();
retry = false;
}
catch (InterruptedException e)
{
}
}
}
}
if (event.getAction() == MotionEvent.ACTION_DOWN)
//set the x and y of your object here when action down
p.posX = event.getX();
p.posY = event.getY();
}
you can listen to the ACTION_MOVE event with GestureDetector http://developer.android.com/reference/android/view/GestureDetector.html

Android Multitouch - Irregular Finger Tracking

I have designed a simple multitouch system that tracks finger location, and their status on the screen. The way that the program is designed, when two fingers are put down, they are assigned a number based on the order of placement. If finger one is lifted, finger two is still called finger two.
This code works perfectly part of the time. Meaning that I'll run my application in testing and sometimes multitouch works every time, and sometimes using more than one finger makes the application completely fail. It is irregularly reliable.
I am curious if this is a hardware error, or something about the way that I am handling touch input. The source is all available below.
Thanks for any help that you can offer!
-Nathan Tornquist
EDIT: I think that the issue may be with the way that eventListeners are registered and unregistered. Occasionally the application will crash, and work perfectly again when I reopen it.
EDIT2: The issue is simply how irregular the program is. Sometimes when I open it, it tracks the fingers perfectly. The first finger put down stays number 1, the second finger stays number 2, etc. Regardless of if you lift finger 1 after placing 2, the numbers stay assigned. Other times you'll place two fingers and which finger is 1 and which is 2 changes. The application seems to lose track of them, and the numbers switch as you leave the fingers on the screen.
EDIT3: I have tried "Touchscreen boosters" to try to try the screen to always respond correctly. It did not solve the problem.
EDIT4: After more testing, this is clearly a code error. When the application is first started, it always works perfectly. After the device is locked (when the activity is paused) and then unlocked (when the activity is resumed) multitouch stops working and I get an application that works well for single touch and is confused by multitouch.
MultitouchGameFixActivity.java
package com.nathantornquist.multitouchgame;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class MultiTouchGameFixActivity extends Activity{
/** Called when the activity is first created. */
MainGamePanel viewPanel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Window state functions.
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//This works without declaring a viewPanel instance here.
//The instance declaration is needed to pass the
//onPause and onResume commands though.
viewPanel = new MainGamePanel(this);
setContentView(viewPanel);
}
//Restarts the accelerometer after onPause
protected void onResume() {
super.onResume();
viewPanel.resume(this);
}
//Standard Method run when the Application loses focus.
//This runs the pause() function in the viewPanel so that
//the accelerometer can be paused.
protected void onPause() {
super.onPause();
viewPanel.pause();
}
protected void onDestroy() {
super.onDestroy();
viewPanel.destroy();
}
}
MainThread.java
package com.nathantornquist.multitouchgame;
import com.nathantornquist.multitouchgame.MainGamePanel;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
public class MainThread extends Thread {
private SurfaceHolder surfaceHolder;
private MainGamePanel gamePanel;
private boolean running;
public boolean pleaseWait = true;
public void setRunning(boolean running) {
this.running = running;
}
public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
super();
this.surfaceHolder = surfaceHolder;
this.gamePanel = gamePanel;
}
#Override
public void run()
{
Canvas canvas;
while (running) {
if(!pleaseWait) {
canvas = null;
// try locking the canvas for exclusive pixel editing on the surface
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
// update game state
this.gamePanel.update();
// draws the canvas on the panel
this.gamePanel.onDraw(canvas);
}
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
else {
synchronized (this) {
try {
wait();
} catch (Exception e) { }
}
}
}
}
}
MainGamePanel.java
package com.nathantornquist.multitouchgame;
import android.R.string;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MainGamePanel extends SurfaceView implements SensorEventListener, SurfaceHolder.Callback
{
//Variable Declarations.
private MainThread thread;
public int screenWidth;
public int screenHeight;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
Paint paint;
public int fingerOneDown;
public int fingerTwoDown;
public int fingerThreeDown;
public int fingerFourDown;
public float fingerOneX;
public float fingerOneY;
public float fingerTwoX;
public float fingerTwoY;
public float fingerThreeX;
public float fingerThreeY;
public float fingerFourX;
public float fingerFourY;
public MainGamePanel(Context context)
{
super(context);
getHolder().addCallback(this);
thread = new MainThread(getHolder(),this);
paint = new Paint();
paint.setAntiAlias(true);
Display display = ((Activity) context).getWindowManager().getDefaultDisplay();
screenWidth = display.getWidth();
screenHeight = display.getHeight();
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
fingerOneDown = 0;
fingerTwoDown = 0;
fingerThreeDown = 0;
fingerFourDown = 0;
fingerOneX = 0;
fingerOneY = 0;
fingerTwoX = 0;
fingerTwoY = 0;
fingerThreeX = 0;
fingerThreeY = 0;
fingerFourX = 0;
fingerFourY = 0;
setFocusable(true);
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
//continue the thread
synchronized (thread) {
thread.pleaseWait = false;
thread.notifyAll();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
//pause the thread
synchronized (thread) {
thread.pleaseWait = true;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
int pointerId = event.getPointerId(pointerIndex);
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
Log.d("pointer id - down",Integer.toString(pointerId));
if (pointerId == 0)
{
fingerOneDown = 1;
fingerOneX = event.getX(pointerIndex);
fingerOneY = event.getY(pointerIndex);
}
if (pointerId == 1)
{
fingerTwoDown = 1;
fingerTwoX = event.getX(pointerIndex);
fingerTwoY = event.getY(pointerIndex);
}
if(pointerId == 2)
{
fingerThreeDown = 1;
fingerThreeX = event.getX(pointerIndex);
fingerThreeY = event.getY(pointerIndex);
}
if(pointerId == 3)
{
fingerFourDown = 1;
fingerFourX = event.getX(pointerIndex);
fingerFourY = event.getY(pointerIndex);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
Log.d("pointer id - cancel",Integer.toString(pointerId));
if (pointerId == 0)
{
fingerOneDown = 0;
fingerOneX = event.getX(pointerIndex);
fingerOneY = event.getY(pointerIndex);
}
if (pointerId == 1)
{
fingerTwoDown = 0;
fingerTwoX = event.getX(pointerIndex);
fingerTwoY = event.getY(pointerIndex);
}
if(pointerId == 2)
{
fingerThreeDown = 0;
fingerThreeX = event.getX(pointerIndex);
fingerThreeY = event.getY(pointerIndex);
}
if(pointerId == 3)
{
fingerFourDown = 0;
fingerFourX = event.getX(pointerIndex);
fingerFourY = event.getY(pointerIndex);
}
break;
case MotionEvent.ACTION_MOVE:
int pointerCount = event.getPointerCount();
for(int i = 0; i < pointerCount; ++i)
{
pointerIndex = i;
pointerId = event.getPointerId(pointerIndex);
Log.d("pointer id - move",Integer.toString(pointerId));
if(pointerId == 0)
{
fingerOneDown = 1;
fingerOneX = event.getX(pointerIndex);
fingerOneY = event.getY(pointerIndex);
}
if(pointerId == 1)
{
fingerTwoDown = 1;
fingerTwoX = event.getX(pointerIndex);
fingerTwoY = event.getY(pointerIndex);
}
if(pointerId == 2)
{
fingerThreeDown = 1;
fingerThreeX = event.getX(pointerIndex);
fingerThreeY = event.getY(pointerIndex);
}
if(pointerId == 3)
{
fingerFourDown = 1;
fingerFourX = event.getX(pointerIndex);
fingerFourY = event.getY(pointerIndex);
}
}
break;
}
return true;
}
#Override
protected void onDraw(Canvas canvas)
{
paint.setColor(Color.WHITE);
paint.setStyle(Style.FILL);
canvas.drawPaint(paint);
if (fingerOneDown == 1)
{
paint.setColor(Color.BLUE);
paint.setTextSize(20);
canvas.drawText("1", fingerOneX, fingerOneY - 30, paint);
}
if (fingerTwoDown == 1)
{
paint.setColor(Color.RED);
paint.setTextSize(20);
canvas.drawText("2", fingerTwoX, fingerTwoY - 30, paint);
}
if (fingerThreeDown == 1)
{
paint.setColor(Color.GREEN);
paint.setTextSize(20);
canvas.drawText("3", fingerThreeX, fingerThreeY - 30, paint);
}
if (fingerFourDown == 1)
{
paint.setColor(Color.BLACK);
paint.setTextSize(20);
canvas.drawText("4", fingerFourX, fingerFourY - 30, paint);
}
}
public void update() {
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
#Override
public void onSensorChanged(SensorEvent event) {
}
public void pause() {
mSensorManager.unregisterListener(this);
}
public void resume(Context context) {
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
}
public void destroy() {
thread.setRunning(false);
if (thread != null)
{
Thread killThread = thread;
thread = null;
killThread.interrupt();
}
}
}
I'll throw out some ideas, even though my android skills are a bit lacking...
How are you testing this? On a real device I guess? Can this be tested on the emulator? It seems like it would be prone to hardware issues.
If the there is a glitch and the screen looses track of the fingers even just for a millisec, wont it start over with indexing the fingers? If that is the case it would number the fingers pretty randomly causing the issues you are experiencing.
Some ideas:
Dont track the index of fingers, just keep list of the fingers and their relative positions. But I guess part of the game requires you to keep an index of each finger.
Log if you loose connection with a finger, that will show if this is an isse. I guess MotionEvent.ACTION_POINTER_UP is the event you should log then. In fact, I'd add a log for each event possible just to see what is happening.
Add a method that tries to determine which finger has been placed on the screen, i.e it compares the current position of the finger with the known values of previous fingers and if its within a threshold, assume its the same finger. Probably want to add a timer to "forget" previous fingers aswell.
I guess you have been looking at the multitouch example on Android developer blog to make sure you are using the APIs correctly?
Hope it may give you some help.
The problem is solved by disabling WidgetLocker. That application seems to mess up some programs occasionally. I've noticed issues in other applications as well when I lock the screen while playing a game.

Categories