I want to move from activity that has thread in it (actually it's in the activity's SurfaceView) to other activity. But when I run it, sometimes it works, sometimes it's not responding (it moves to other activity but the activity freezes). I really frustrated right now. I have searched everywhere but I can't find the answer to my problem. Somebody please help me. :(
I'll show you the code. If you need other pieces of code I'll show it to you.
Here is the Activity code:
public class MapActivity extends Activity {
private static final String TAG = MapActivity.class.getSimpleName();
private DisplayMetrics metrics;
private MapView mapView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
DrawableManager.initInstance(getApplicationContext());
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
Log.d(TAG, "start game activity");
metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics); // dapetin
// ukuran
// layar
// mapView =
// new
// MapView(this,
// metrics.widthPixels,
// metrics.heightPixels);
MapView.initInstance(getApplicationContext(), metrics.widthPixels,
metrics.heightPixels);
setContentView(MapView.getInstance());
}
#Override
public void onPause() {
super.onPause();
MapView.getInstance().thread.setRunning(false);
}
}
Here is the SurfaceView code (I don't give you all part of the code because it will be too long):
public class MapView extends SurfaceView implements SurfaceHolder.Callback {
public final Random rand;
private Resources res;
private static MapView instance;
private static int screenWidth;
private static int screenHeight;
private static final String TAG = MapView.class.getSimpleName();
public mapThread thread;
private Matrix matrix = new Matrix();
public static Block blockTile[][];
private Player player;
public static int tileSize;
public static boolean isMalam = false;
public static boolean isAdaMonster = false;
private ArrayList<Monster> monsters;
public static int leftMargin;
public static final int nTileH = 10;
public static final int nTileW = 17;
private int ctrMonster = 0;
private int ctrDetik = 0;
private int ctrMenit = 0;
private int ctrJam = 6;
private int ctrHari = 0;
private static boolean isOutdoor;
private Pair<Float, Float> tapPos;
private Pair<Float, Float> tapPos2;
private Context parentActivity;
private boolean isTouch = false;
private boolean Found = false;
private Street street;
private Store store;
private Combinatorium combinatorium;
private Stadium stadium;
private Home home;
private AreaLuar3 arealuar3;
private AreaLuar2 arealuar2;
private AreaLuar1 arealuar1;
private String dataMap[];
public static String currentMap;
public MapView(Context context, int scrWidth, int scrHeight) {
super(context);
parentActivity = context;
blockTile = new Block[nTileW][nTileH];
screenWidth = scrWidth;
screenHeight = scrHeight;
int sTile = scrHeight / nTileH;
tileSize = sTile;
getHolder().addCallback(this);
res = context.getResources();
rand = new Random();
dataMap = new String[15];
currentMap = "street";
player = new Player(7, 4);
build();
setFocusable(true);
}
public void surfaceCreated(SurfaceHolder arg0) {
// inisialisasi thread
initThread();
Log.d(TAG, "surface created");
}
public void surfaceDestroyed(SurfaceHolder arg0) {
releaseThread();
Log.d(TAG, "surface destroyed");
}
public static void initInstance(Context context, int W, int H) {
assert instance == null;
instance = new MapView(context, W, H);
}
public static MapView getInstance() {
assert instance != null;
return instance;
}
// inisialisasi thread
public void initThread() {
if (thread == null || !thread.isAlive()) {
thread = new mapThread(getHolder(), this);
thread.start();
}
thread.setRunning(true);
}
private void releaseThread() {
boolean retry = true;
while (retry)
try {
thread.join();
retry = false;
thread = null;
} catch (InterruptedException e) {
}
}
public void update() {
ctrDetik++;
if (ctrDetik > 59) {
ctrDetik = 0;
ctrMenit++;
if (ctrMenit > 59) {
ctrMenit = 0;
ctrJam++;
cekMalam();
if (ctrJam > 23) {
ctrJam = 0;
ctrHari++;
}
}
}
if (!Found && isAdaMonster) {
ctrMonster++;
for (Monster mons : monsters) {
if (mons.cekEqualBlock(player)) {
Found = true;
Intent test = new Intent(parentActivity, MainMenu.class);
test.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
parentActivity.startActivity(test);
}
if (Found)
break;
if (ctrMonster == 30) {
ctrMonster = 0;
monsters.get(0).moveRandom();
monsters.get(1).moveCloser(player.getXBlock(),
player.getYBlock());
}
}
}
if (isTouch)
if (Math.abs(tapPos2.first - tapPos.first) > 100
|| Math.abs(tapPos2.second - tapPos.second) > 100)
if (Math.abs(tapPos2.first - tapPos.first) > Math
.abs(tapPos2.second - tapPos.second)) {
if (tapPos2.first - tapPos.first > 0)
player.move(2);
else
player.move(4);
} else if (Math.abs(tapPos2.first - tapPos.first) < Math
.abs(tapPos2.second - tapPos.second))
if (tapPos2.second - tapPos.second > 0)
player.move(3);
else
player.move(1);
}
}
Here is the thread code:
package com.map;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
public class mapThread extends Thread {
private boolean running;
private SurfaceHolder surfaceHolder;
private MapView mapView;
private final static int MAX_FPS = 60; // fps yang
// diinginkan
private final static int MAX_FRAME_SKIPS = 5; // maksimum
// jumlah
// frame
// yang bisa
// diskip
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
public mapThread(SurfaceHolder surfaceHolder, MapView gameMapView) {
super();
this.surfaceHolder = surfaceHolder;
mapView = gameMapView;
}
public void setRunning(boolean val) {
running = val;
}
#Override
public void run() {
Canvas canvas;
long beginTime; // waktu mulai siklus
long timeDiff; // waktu yang diperlukan satu siklus untuk selesai
int sleepTime; // ms untuk tidur(<0 jika ketinggalan)
int framesSkipped; // jumlah frame yang akan diskip
sleepTime = 0;
while (running) {
canvas = null;
// ngunci canvas untuk digambar
try {
canvas = surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // reset jumlah frame yang pengen diskip
// update game state
// draw canvas di panel
mapView.update();
mapView.render(canvas);
// hitung berapa lama satu siklus
timeDiff = System.currentTimeMillis() - beginTime;
// hitung waktu tidur
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0)
// tidurin thread selama waktu tidur tsb
// cycle lebih cepat dari fps
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// ketinggalan fps, update tanpa manggil render
mapView.update();
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
}
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null)
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
startActivity is executed in update method in SurfaceView code. What should I do? Really needs help here.
SurfaceView extends View, so you can call the View.post() method.
In the code where you currently startThe activity you want to do this.
You can call another function to do this if you want it to be a little cleaner.
post(new Runnable() {
#Override
public void run() {
Intent test = new Intent(parentActivity, MainMenu.class);
test.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
parentActivity.startActivity(test);
});
This will call startActivity on the mainThread.
Related
I am working on an Android project which takes the heart rate. The application runs fine but it continuously does processing and gives different readings. I want the application to stop processing the image after a set time. I have used the postDelayed method, but it didn't work or maybe I didn't use it right. Maybe someone can help. Below is the class that does the image processing. How do I stop it from processing after about 20 seconds?
/**
* This class extends Activity to handle a picture preview, process the preview
* for a red values and determine a heart beat.
*/
public class HeartRateMonitor extends Activity {
private static final String TAG = "HeartRateMonitor";
private static final AtomicBoolean processing = new AtomicBoolean(false);
private static SurfaceView preview = null;
private static SurfaceHolder previewHolder = null;
private static Camera camera = null;
private static View image = null;
private static TextView text = null;
private static WakeLock wakeLock = null;
private static int averageIndex = 0;
private static final int averageArraySize = 4;
private static final int[] averageArray = new int[averageArraySize];
public static enum TYPE {
GREEN, RED
};
private static TYPE currentType = TYPE.GREEN;
public static TYPE getCurrent() {
return currentType;
}
private static int beatsIndex = 0;
private static final int beatsArraySize = 3;
private static final int[] beatsArray = new int[beatsArraySize];
private static double beats = 0;
private static long startTime = 0;
/**
* {#inheritDoc}
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_heart_beat_rate);
preview = (SurfaceView) findViewById(R.id.preview);
previewHolder = preview.getHolder();
previewHolder.addCallback(surfaceCallback);
previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
image = findViewById(R.id.image);
text = (TextView) findViewById(R.id.text);
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "DoNotDimScreen");
}
/**
* {#inheritDoc}
*/
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
/**
* {#inheritDoc}
*/
#Override
public void onResume() {
super.onResume();
wakeLock.acquire();
camera = Camera.open();
startTime = System.currentTimeMillis();
}
/**
* {#inheritDoc}
*/
#Override
public void onPause() {
super.onPause();
wakeLock.release();
camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera = null;
}
private PreviewCallback previewCallback = new PreviewCallback() {
/**
* {#inheritDoc}
*/
#Override
public void onPreviewFrame(byte[] data, Camera cam) {
if (data == null) throw new NullPointerException();
Camera.Size size = cam.getParameters().getPreviewSize();
if (size == null) throw new NullPointerException();
if (!processing.compareAndSet(false, true)) return;
int width = size.width;
int height = size.height;
int imgAvg = ImageProcessing.decodeYUV420SPtoRedAvg(data.clone(), height, width);
// Log.i(TAG, "imgAvg="+imgAvg);
if (imgAvg == 0 || imgAvg == 255) {
processing.set(false);
return;
}
int averageArrayAvg = 0;
int averageArrayCnt = 0;
for (int i = 0; i < averageArray.length; i++) {
if (averageArray[i] > 0) {
averageArrayAvg += averageArray[i];
averageArrayCnt++;
}
}
int rollingAverage = (averageArrayCnt > 0) ? (averageArrayAvg / averageArrayCnt) : 0;
TYPE newType = currentType;
if (imgAvg < rollingAverage) {
newType = TYPE.RED;
if (newType != currentType) {
beats++;
// Log.d(TAG, "BEAT!! beats="+beats);
}
} else if (imgAvg > rollingAverage) {
newType = TYPE.GREEN;
}
if (averageIndex == averageArraySize) averageIndex = 0;
averageArray[averageIndex] = imgAvg;
averageIndex++;
// Transitioned from one state to another to the same
if (newType != currentType) {
currentType = newType;
image.postInvalidate();
}
long endTime = System.currentTimeMillis();
double totalTimeInSecs = (endTime - startTime) / 1000d;
if (totalTimeInSecs >= 10) {
double bps = (beats / totalTimeInSecs);
int dpm = (int) (bps * 60d);
if (dpm < 20 || dpm > 120) {
startTime = System.currentTimeMillis();
beats = 0;
processing.set(false);
return;
}
// Log.d(TAG,
if (beatsIndex == beatsArraySize) beatsIndex = 0;
beatsArray[beatsIndex] = dpm;
beatsIndex++;
int beatsArrayAvg = 0;
int beatsArrayCnt = 0;
for (int i = 0; i < beatsArray.length; i++) {
if (beatsArray[i] > 0) {
beatsArrayAvg += beatsArray[i];
beatsArrayCnt++;
}
}
startTime = System.currentTimeMillis();
beats = 0;
final int beatsAvg = (beatsArrayAvg / beatsArrayCnt);
// final ArrayList HeartRateResultPojoObjArrayList = new ArrayList();
//maybe here
final AlertDialog alertDialog = new AlertDialog.Builder(HeartRateMonitor.this).create();
alertDialog.setTitle("Your Heart Rate is:");
alertDialog.setMessage(String.valueOf(beatsAvg) + " bpm");
alertDialog.setIcon(R.drawable.green_icon);
alertDialog.setButton(BUTTON_NEUTRAL, "Save",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent newActivityIntent = new Intent(HeartRateMonitor.this, History.class);
startActivity(newActivityIntent);
}
});
alertDialog.setButton(BUTTON_POSITIVE, "Share Result with a Doctor", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
try {
Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.putExtra("sms_body", "My Heart Rate is " + String.valueOf(beatsAvg) + "bpm. " + " Any Advice for me?");
sendIntent.setType("vnd.android-dir/mms-sms");
startActivity(sendIntent);
} catch (Exception e) {
Toast.makeText(getApplicationContext(),
"SMS failed, please try again later!",
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
});
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
alertDialog.show();
}
}, 25000);
}
processing.set(false);
}
};
private SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
/**
* {#inheritDoc}
*/
#SuppressLint("LongLogTag")
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(previewHolder);
camera.setPreviewCallback(previewCallback);
} catch (Throwable t) {
Log.e("PreviewDemo-surfaceCallback", "Exception in setPreviewDisplay()", t);
}
}
/**
* {#inheritDoc}
*/
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Camera.Parameters parameters = camera.getParameters();
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
Camera.Size size = getSmallestPreviewSize(width, height, parameters);
if (size != null) {
parameters.setPreviewSize(size.width, size.height);
Log.d(TAG, "Using width=" + size.width + " height=" + size.height);
}
camera.setParameters(parameters);
camera.startPreview();
}
/**
* {#inheritDoc}
*/
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// Ignore
}
};
private Camera.Size getSmallestPreviewSize(int width, int height, Camera.Parameters parameters) {
Camera.Size result = null;
for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
if (size.width <= width && size.height <= height) {
if (result == null) {
result = size;
} else {
int resultArea = result.width * result.height;
int newArea = size.width * size.height;
if (newArea < resultArea) result = size;
}
}
}
return result;
}
}
What method should I use? And where in the above code should I place it to achieve my goal?
It depends on the type of class that is running, if it is a AsyncTask ,
cancel ( true) cancels the current action.
If you want you can use it inside the method onCancel () in one of these dialogues.
When cancel ( true) is called it changes the status canceled , so wrap your logic in a
while ( ! isCancelled ( ) ) {
// do your logic
}
guys. I'm playing around with making my very first Android game, but stumbled into a problem. The framerate seems to have random lag spikes. If I comment the background(s) out the framerate gets much smoother. I've looked around SO and can't find anything to solve my problems. I have a feeling it has something to do with allocating a specific amount of time every time I draw, but I don't know how to properly implement such a feature. Any suggestions? Btw, tryed hardware ac, anti etc.
This is the class that starts the surfaceview :
package com.example.glassrunner;
Imports Here
public class Game extends Activity
{
MySurfaceView mySurfaceView;
public SoundPool spool;
private int soundID;
int length=0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
mySurfaceView = new MySurfaceView(this);
setContentView(mySurfaceView);
}
#Override
protected void onResume()
{
// TODO Auto-generated method stub
super.onResume();
mySurfaceView.onResumeMySurfaceView();
}
#Override
protected void onPause()
{
// TODO Auto-generated method stub
super.onPause();
mySurfaceView.onPauseMySurfaceView();
}
#Override
protected void onDestroy()
{
super.onDestroy();
mySurfaceView = null;
}
}
This is the surfaceview class :
package com.example.glassrunner;
Imports here
public class MySurfaceView extends SurfaceView implements Runnable
{
public static boolean gameOver = false;
SurfaceHolder surfaceHolder;
Thread thread = null;
public Integer score=0;
public SoundPool spool;
private int soundID;
int length=0;
public static MediaPlayer mp;
volatile boolean running = false;
int Yposition = 450;
int Xposition = 50;
Paint textPaint;
long mLastTime;
Bitmap background;
Bitmap background2;
Bitmap lines;
Bitmap runSprite;
Bitmap box;
Paint bitmapPaint ;
Paint textPaint2;
Bitmap scaledBackground ;
Bitmap scaledBackground2 ;
Bitmap scaledLines ;
Bitmap scaledBox;
Canvas canvas;
Paint paint;
int SpX=0;
int SpY=0;
Bitmap[][] sprite;
/** Variables for the counter */
int frameSamplesCollected = 0;
int frameSampleTime = 0;
int fps = 0;
int speed = 5;
Toast GameOverToast;
Context context;
MediaPlayer mMediaPlayer;
public MySurfaceView(Context context)
{
super(context);
this.context = context;
// TODO Auto-generated constructor stub
surfaceHolder = getHolder();
surfaceHolder.setFormat(PixelFormat.RGB_565);
CharSequence text = "Game Over!";
int duration = Toast.LENGTH_SHORT;
GameOverToast = Toast.makeText(context, text, duration);
spool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
soundID = spool.load(context, R.raw.jump, 1);
mp = MediaPlayer.create(context, R.raw.saturdaymorningfunk);
initialization();
}
public void initialization()
{
mp.setLooping(true);
mp.start();
Options options = new Options();
options.inSampleSize = 1/4;
options.inPreferredConfig = Bitmap.Config.RGB_565;
background=BitmapFactory.decodeResource(getResources(),R.drawable.background,options);
lines=BitmapFactory.decodeResource(getResources(),R.drawable.lines);// getting the png from drawable folder
background2=BitmapFactory.decodeResource(getResources(),R.drawable.background2,options);
runSprite=BitmapFactory.decodeResource(getResources(),R.drawable.runsprite);
box=BitmapFactory.decodeResource(getResources(),R.drawable.box);
bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // tool for painting on the canvas
bitmapPaint.setAntiAlias(true);
bitmapPaint.setFilterBitmap(true);
textPaint = new Paint();
textPaint.setColor(Color.RED);
textPaint.setTextSize(32);
textPaint2 = new Paint();
textPaint2.setColor(Color.BLUE);
textPaint2.setTextSize(50);
scaledBackground = Bitmap.createScaledBitmap(background, 2560, 500, true);
scaledBackground2 = Bitmap.createScaledBitmap(background2, 2560, 400, true);
scaledLines = Bitmap.createScaledBitmap(lines, 2560, 30, true);
runSprite = Bitmap.createScaledBitmap(runSprite, 1400, 1000, true);
scaledBox = Bitmap.createScaledBitmap(box, 100, 100, true);
sprite = new Bitmap[4][7];
for(int row=0;row<=3;row++)
{
for(int col=0;col<=6;col++)
{
sprite[row][col] = Bitmap.createBitmap(runSprite, SpX, SpY, 200, 250);
SpX+=200;
}
SpX=0;
SpY+=250;
}
}
public void onResumeMySurfaceView()
{
mp.seekTo(length);
mp.start();
running = true;
thread = new Thread(this);
thread.start();
}
public void onPauseMySurfaceView()
{
mp.pause();
length=mp.getCurrentPosition();
boolean retry = true;
running = false;
while(retry){
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void onDestroyMySurfaceView()
{
mp.stop();
running = false;
thread = null;
thread.stop();
}
private void fps()
{
long now = System.currentTimeMillis();
if (mLastTime != 0)
{
//Time difference between now and last time we were here
int time = (int) (now - mLastTime);
frameSampleTime += time;
frameSamplesCollected++;
//After 10 frames
if (frameSamplesCollected == 10)
{
//Update the fps variable
fps = (int) (10000 / frameSampleTime);
//Reset the sampletime + frames collected
frameSampleTime = 0;
frameSamplesCollected = 0;
}
}
mLastTime = now;
}
public boolean pressDown = false;
public long pressTime;
public boolean onTouchEvent(MotionEvent event)
{
if (event != null)
{
if (event.getAction() == MotionEvent.ACTION_DOWN)
{ if(Yposition == orgPos)
{
spool.play(soundID, 15, 15, 1, 0, 1f);
pressDown = true;
pressTime = System.currentTimeMillis();
}
}else if (event.getAction() == MotionEvent.ACTION_UP)
{
pressDown = false;
}
}
return true;
}
int x=0;
int y=100;
int x2=0;
int y2=20;
int row=0;
int col=0;
int limit = 100;
int orgPos = 450;
int Xbox = 1280;
int Ybox = 580;
Random r = new Random();
int RBox;
public static String Fscore;
boolean onTop = false;
long now;
long start;
long stop;
long time ;
int spritePosition = 0 ;
int spriteSize;
#Override
public void run()
{
while(running)
{
canvas = null;
if(surfaceHolder.getSurface().isValid())
{
canvas = surfaceHolder.lockCanvas();
fps(); // fps
// Update screen parameters
update();
draw();
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
public void update()
{
if(score<500)
{
speed = 7;
}
else if(score%500 == 0)
{
speed = 7 + (score / 500);
}
if(col==6)
{
row++;
col=0;
}
if(row==4)
{
row=0;
}
score++;
Fscore = score.toString();
if(x>-1280)
{
x-=speed;
}else if(x<=-1280)
{
x=0;
}
if(x2>-1280)
{
x2-=5;
}else if(x2<=-1280)
{
x2=-0;
}
RBox = r.nextInt(999)+1280;
if(Xbox > -100)
{
Xbox-=speed;
}else if(Xbox<=-100)
{
Xbox=RBox;
}
if( (Xposition + 200 == Xbox +40 )&&(Yposition + 250 > Ybox+20)||( Xposition+200<=Xbox+70)&&( Xposition+200>=Xbox+20)&&(Yposition + 250 > Ybox+30) ) // collision
{
GameOverToast.show();
running = false;
spool.release();
mp.release();
Looper.prepare();
Intent database = new Intent(context, MainHighscore.class);
database.putExtra("score", Fscore);
context.startActivity(database);
onDestroyMySurfaceView();
}
now = System.currentTimeMillis();
if(( now - pressTime) <= 600)
{
if(Yposition > limit)
{
Yposition -= 10;
}
}
onTop = false;
if((now - pressTime) >= 600 && (now - pressTime) <= 1200)
{
if(!(Yposition == orgPos))
{
if(Yposition+250 >= Ybox && Xposition+200>=Xbox+70 && Xposition <= Xbox+40)
{
onTop=true;
Yposition = 340;
}else
{
Yposition += 10;
}
}
}
if((now - pressTime) >= 1200)
{
if(Yposition < 450) Yposition +=10;
else Yposition = 450;
}
}
public void draw()
{
canvas.drawColor(Color.WHITE);
//canvas.drawBitmap(scaledBackground, x2,y2, bitmapPaint);
canvas.drawBitmap(scaledBackground2, x,y, bitmapPaint);
canvas.drawBitmap(scaledLines, x,650, bitmapPaint);
canvas.drawText(Fscore, 1050, 50, textPaint2);
canvas.drawText(fps + " fps", getWidth() / 2, getHeight() / 2, textPaint);
canvas.drawBitmap(sprite[row][col],Xposition,Yposition,bitmapPaint );
canvas.drawBitmap(scaledBox,Xbox,Ybox,bitmapPaint);
col++;
}
}
I think your problem might be actually the moving part. Your just drawing too much stuff, and the surfaceView is not meant for that.
Hi in my application I have an explosion animation that comes up extremely often. when creating an explosion I load 3 images from resources then once the explosion animation is over I recycle those 3 images. I am constantly doing this and have noticed framerate drops. Is their a better way of doing this like static bitmaps or something?
I once made an application, a simple canvas on which bombs exploded.
I used a tiled bitmap composed of each step of the explosion and drew only a part of it, which changes often in order to create the animation effect.
It updates steps automatically following currentTimestamp
So this is the explosion class:
public class ExplosionAnimated {
private static final String TAG = ExplosionAnimated.class.getSimpleName();
private Bitmap mBitmap;
private Rect mSourceRect;
private int mFrameCountX;
private int mFrameCountY;
private int mCurrentFrame;
private long mFrameTicker;
private int mFramePeriod;
private int mSpriteWidth;
private int mSpriteHeight;
private int mX;
private int mY;
private boolean mFinished = false;
public ExplosionAnimated(Bitmap pBitmap, int pX, int pY,
int pFrameCountX, int pFrameCountY, int pFps) {
this.mBitmap = pBitmap;
this.mX = pX;
this.mY = pY;
this.mCurrentFrame = 0;
this.mFrameCountX = pFrameCountX;
this.mFrameCountY = pFrameCountY;
this.mSpriteWidth = pBitmap.getWidth() / pFrameCountX;
this.mSpriteHeight = pBitmap.getHeight() / pFrameCountY;
this.mSourceRect = new Rect(0, 0, this.mSpriteWidth, this.mSpriteHeight);
this.mFramePeriod = 1000 / pFps;
this.mFrameTicker = 0l;
}
public void update(long gameTime) {
if (gameTime > this.mFrameTicker + this.mFramePeriod) {
this.mFrameTicker = gameTime;
this.mCurrentFrame++;
if (this.mCurrentFrame >= this.mFramePeriod) {
this.mCurrentFrame = 0;
this.mFinished = true;
}
}
if (!this.mFinished) {
this.mSourceRect.left = this.mCurrentFrame * this.mSpriteWidth;
this.mSourceRect.right = this.mSourceRect.left + this.mSpriteWidth;
}
}
public void draw(Canvas canvas) {
Rect destRect = new Rect(this.mX, this.mY,
this.mX + this.mSpriteWidth,
this.mY + this.mSpriteHeight);
canvas.drawBitmap(this.mBitmap, this.mSourceRect, destRect, null);
}
public boolean isFinished() {
return this.mFinished;
}
}
These are methods from an object (Bomb.java for exemple) that starts explosion and draw it:
public void explode(Context pContext, Canvas pCanvas) {
this.mState = State.EXPLODING;
this.mExplosion = new ExplosionAnimated(this.mExplosionBitmap,
(int) this.mX, (int) this.mY, 7, 3, 7);
}
public void doDraw(Canvas pCanvas) {
if (this.mState == State.EXPLODING) {
if (this.mExplosion.isFinished()) {
this.mState = State.EXPLODED;
} else {
this.mExplosion.update(System.currentTimeMillis());
this.mExplosion.draw(pCanvas);
}
} else {
pCanvas.drawBitmap(this.mBombBitmap, this.mX, this.mY, null);
}
}
I used a Thread and a SurfaceView to continually draw the bomb (or the explosion), giving just the currentTimestamp to update the explosion.
I hope it helps, and if you need I can show and explain more code
I'm developing a game which includes sprites.
currently I'm loading a bitmap and using Rectangle to get the right frame from the bitmap.
the problem is loading bitmaps takes too much memory and I wont be able to load several sprites at the same time.
what are my alternatives for doing sprite in Android?
Try this one:
import org.cocos2d.layers.CCLayer;
public class GameLayer extends CCLayer{
CCSprite mSprite;
protected GameLayer() {
super();
CGSize winSize = CCDirector.sharedDirector().winSize();
mSprite = new CCSprite("image.png");
mSprite.setPosition(CGPoint.ccp(mSprite.getContentSize().width/2.0f, mSprite.getContentSize().height/2.0f));
addChild(mSprite);
}
/* It is Sprite class */
import android.graphics.*;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
public class Sprite {
int x;
private int y;
private boolean visibility = true;
private Bitmap sprite;
private int verticalFrame,horizontalFrame;
private int frameWidth,frameHeight;
private int sequence[] = new int[1];
private int maxFrame = 0;
private int currentFrame;
private int color;
private Rect src;
private Rect dest;
private int frameCount;
private Paint p = new Paint();
private int spInitX,spInitY;
private int rotate = 0;
private float currentRotateAngle = 0;
//private Graphics g;
public float velocityY;
public float velocityX;
public int height;
public Sprite(Bitmap sprite){
this.sprite = sprite;
this.frameWidth = sprite.getWidth();
this.frameHeight = sprite.getHeight();
setFrameCount();
}
public Sprite(Bitmap sprite,int frameWidth,int frameHeight){
this.sprite = sprite;
this.frameWidth = frameWidth;
this.frameHeight = frameHeight;
setFrameCount();
}
public void rotate(float angle){
currentRotateAngle = angle;
}
public void setImage(Bitmap bm,int frameWidth,int frameHeight){
this.sprite = bm;
this.frameWidth = frameWidth;
this.frameHeight = frameHeight;
}
public Bitmap getBitmap(){
return sprite;
}
public void paint(Canvas canvas){
dest.offsetTo(getX(), getY());
// g.drawImage(sprite, x, y,src.left,src.top,frameWidth,frameHeight);
canvas.drawBitmap(sprite, src, dest, null);
}
public int getMaxFrame(){
return maxFrame;
}
public int getFrameSequenceLength(){
return sequence.length;
}
public void setFrameSequence(int seq[]){
sequence = seq;
}
public void previousFrame(){
if(sequence.length > 1){
if(frameCount > 0){
setFrame(sequence[frameCount]);
frameCount--;
}else{
frameCount = sequence.length - 1;
setFrame(sequence[frameCount]);
}
}else{
setFrame(frameCount);
if(frameCount > 0){
frameCount++;
}else{
frameCount = maxFrame - 1;
}
}
}
public void setPixel(int x,int y){
spInitX = x;
spInitY = y;
}
public void nextFrame(){
if(sequence.length > 1){
if(frameCount < sequence.length){
setFrame(sequence[frameCount]);
frameCount++;
}else{
frameCount = 0;
setFrame(sequence[frameCount]);
}
}else{
setFrame(frameCount);
if(frameCount < maxFrame){
frameCount++;
}else{
frameCount = 0;
}
}
}
public int getFrame(){
return currentFrame;
}
public void setPosition(int x,int y){
this.x = x;
this.y = y;
}
public void setFrameCount(){
verticalFrame = sprite.getHeight() / frameHeight;
horizontalFrame = sprite.getWidth() / frameWidth;
src = new Rect(0,0,frameWidth,frameHeight);
dest = new Rect(0,0,frameWidth,frameHeight);
maxFrame = verticalFrame * horizontalFrame;
}
public void setFrame(int frame){
if(frame < maxFrame){
currentFrame = frame;
}
int hf = currentFrame % horizontalFrame;
int vf = currentFrame / horizontalFrame;
src.left = hf * frameWidth;
src.right = src.left + frameWidth;
src.top = vf * frameHeight;
src.bottom = src.top + frameHeight;
}
public boolean collidesWith(Sprite sp,boolean cl){
int maxHGap = (getWidth() + sp.getWidth())/2;
int maxVGap = (getHeight() + sp.getHeight())/2;
int x = getX() + getWidth()/2;
int y = getY() + getHeight()/2;
int x1 = sp.getX() + sp.getWidth()/2;
int y1 = sp.getY() + sp.getHeight()/2;
if(Math.abs(x - x1) < maxHGap && Math.abs(y - y1) < maxVGap){
return true;
}
return false;
}
public void setVisible(boolean v){
visibility = v;
}
public final boolean isVisible(){
return visibility;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void move(int moveX,int moveY){
setX(getX()+moveX);
setY(getY()+moveY);
//this.y+=y;
//this.x+=x;
}
public final int getWidth(){
return frameWidth;
}
public final int getHeight(){
return frameHeight;
}
public void setEventY(int i) {
// TODO Auto-generated method stub
}
public int getEventY() {
// TODO Auto-generated method stub
return 0;
}
}
/*Create Main Thread Class Also */
import java.text.DecimalFormat;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
public class MainThread extends Thread {
boolean isPaused;
private static final String TAG = MainThread.class.getSimpleName();
// desired fps
private final static int MAX_FPS = 50;
// maximum number of frames to be skipped
private final static int MAX_FRAME_SKIPS = 5;
// the frame period
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
/* Stuff for stats */
private DecimalFormat df = new DecimalFormat("0.##"); // 2 dp
// we'll be reading the stats every second
private final static int STAT_INTERVAL = 1000; // ms
// the average will be calculated by storing
// the last n FPSs
private final static int FPS_HISTORY_NR = 10;
// last time the status was stored
private long lastStatusStore = 0;
// the status time counter
private long statusIntervalTimer = 0l;
// number of frames skipped since the game started
private long totalFramesSkipped = 0l;
// number of frames skipped in a store cycle (1 sec)
private long framesSkippedPerStatCycle = 0l;
// number of rendered frames in an interval
private int frameCountPerStatCycle = 0;
private long totalFrameCount = 0l;
// the last FPS values
private double fpsStore[];
// the number of times the stat has been read
private long statsCount = 0;
// the average FPS since the game started
private double averageFps = 0.0;
// Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
// The actual view that handles inputs
// and draws to the surface
private GameView gv;
// flag to hold game state
private boolean running;
public void setRunning(boolean running) {
this.running = running;
}
public MainThread(SurfaceHolder surfaceHolder, GameView gv) {
super();
this.surfaceHolder = surfaceHolder;
this.gv = gv;
}
public MainThread(Setting setting) {
// TODO Auto-generated constructor stub
}
public void setPause(int i) {
synchronized (gv.getHolder()) {
if (i == 0) {
isPaused = false;
}
if (i == 1) {
isPaused = true;
}
}
}
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
initTimingElements();
long beginTime; // the time when the cycle begun
long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
sleepTime = 0;
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// update game state
this.gv.update();
// render state to the screen
// draws the canvas on the panel
this.gv.render(canvas);
// calculate how long did the cycle take
timeDiff = System.currentTimeMillis() - beginTime;
// calculate sleep time
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
// if sleepTime > 0 we're OK
try {
// send the thread to sleep for a short period
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// we need to catch up
this.gv.update(); // update without rendering
sleepTime += FRAME_PERIOD; // add frame period to check
// if in next frame
framesSkipped++;
}
if (framesSkipped > 0) {
Log.d(TAG, "Skipped:" + framesSkipped);
}
// for statistics
framesSkippedPerStatCycle += framesSkipped;
// calling the routine to store the gathered statistics
storeStats();
}
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
private void storeStats() {
frameCountPerStatCycle++;
totalFrameCount++;
// assuming that the sleep works each call to storeStats
// happens at 1000/FPS so we just add it up
// statusIntervalTimer += FRAME_PERIOD;
// check the actual time
statusIntervalTimer += (System.currentTimeMillis() - statusIntervalTimer);
if (statusIntervalTimer >= lastStatusStore + STAT_INTERVAL) {
// calculate the actual frames pers status check interval
double actualFps = (double) (frameCountPerStatCycle / (STAT_INTERVAL / 1000));
// stores the latest fps in the array
fpsStore[(int) statsCount % FPS_HISTORY_NR] = actualFps;
// increase the number of times statistics was calculated
statsCount++;
double totalFps = 0.0;
// sum up the stored fps values
for (int i = 0; i < FPS_HISTORY_NR; i++) {
totalFps += fpsStore[i];
}
// obtain the average
if (statsCount < FPS_HISTORY_NR) {
// in case of the first 10 triggers
averageFps = totalFps / statsCount;
} else {
averageFps = totalFps / FPS_HISTORY_NR;
}
// saving the number of total frames skipped
totalFramesSkipped += framesSkippedPerStatCycle;
// resetting the counters after a status record (1 sec)
framesSkippedPerStatCycle = 0;
statusIntervalTimer = 0;
frameCountPerStatCycle = 0;
statusIntervalTimer = System.currentTimeMillis();
lastStatusStore = statusIntervalTimer;
// Log.d(TAG, "Average FPS:" + df.format(averageFps));
gv.setAvgFps("FPS: " + df.format(averageFps));
}
}
private void initTimingElements() {
// initialise timing elements
fpsStore = new double[FPS_HISTORY_NR];
for (int i = 0; i < FPS_HISTORY_NR; i++) {
fpsStore[i] = 0.0;
}
Log.d(TAG + ".initTimingElements()",
"Timing elements for stats initialised");
}
}
I am getting an exception here when I try to bring another view to the front. It happens in the last method of my BoardView class where it says if(lives == 0). Can anybody see where I should be calling this bringToFront method from??
public class BoardView extends SurfaceView implements SurfaceHolder.Callback{
Context mContext;
// thread initialization
private BoardThread thread;
Thread timer;
// box variables
Bitmap box =
(BitmapFactory.decodeResource
(getResources(), R.drawable.box));
private int box_x = 140;
private int box_y = 378;
private int boxWidth = box.getWidth();
private int boxHeight = box.getHeight();
// storage
private Vector<Blossom> blossomVector = new Vector<Blossom>();
Iterator<Blossom> dataIterator = blossomVector.iterator();
// counters
private int blossomNum = 0;
private String score;
private int currentScore = 0;
private int lives = 3;
boolean mode = false;
boolean game = false;
OutputStreamWriter out = null;
FileOutputStream fOut = null;
private static final String TAG = "Debug";
final Paint scorePaint = new Paint();
public BoardView(Context context){
super(context);
scorePaint.setColor(Color.BLACK);
scorePaint.setTextSize(12);
scorePaint.setTypeface(Typeface.MONOSPACE);
//surfaceHolder provides canvas that we draw on
getHolder().addCallback(this);
// controls drawings
thread = new BoardThread(getHolder(),this, blossomVector, dataIterator, box_x, box_y,
boxWidth, boxHeight);
timer = new Thread(){
public void run(){
//makes sure the player still has 3 lives left
while(game == false){
uiCallback.sendEmptyMessage(0);
try {
Thread.sleep(2000); // wait two seconds before drawing the next flower
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} //sleep for 2 seconds
}
}
};
timer.start();
//intercepts touch events
setFocusable(true);
}
#Override
public void onDraw(Canvas canvas){
canvas.drawColor(Color.WHITE);
score = "SCORE: " + currentScore;
//note: pay attention to order you draw things
//don't change order or else blossoms will fall
//on top of box, not "into" it.
//display the scoreboard
canvas.drawText(score,240,420,scorePaint);
// uses a synchronized method to prevent concurrent modification
DrawBlossoms(canvas);
canvas.drawBitmap(box, box_x, box_y, null);
}
#Override
public boolean onTouchEvent(MotionEvent event){
//handles movement of box
if(event.getAction() == MotionEvent.ACTION_DOWN){
if(event.getX() > box_x & event.getY() > box_y &
event.getX() < box_x + boxWidth & event.getY() < box_y + boxHeight)
{
mode = true;
}
}
if(event.getAction() == MotionEvent.ACTION_MOVE) {
if(event.getX() > box_x & event.getY() > box_y &
event.getX() < box_x + boxWidth & event.getY() < box_y + boxHeight)
{
mode = true;
}
if(mode == true){
box_x = (int)event.getX();
}
}
if(event.getAction() == MotionEvent.ACTION_UP){
mode = false;
}
invalidate();
return true;
}
#Override
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height ){
Log.v(TAG, "Surface Changed");
//somehow these don't seem to be working
}
#Override
public void surfaceCreated(SurfaceHolder holder){
thread.startRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder){
Log.v(TAG, "Surface Destroyed");
//somehow these don't seem to be working
thread.startRunning(false);
thread.stop();
timer.interrupt();
timer.stop();
}
private Handler uiCallback = new Handler(){
public synchronized void handleMessage(Message msg){
//add a new blossom to the blossom Vector!!
blossomVector.add(new Blossom(
(BitmapFactory.decodeResource
(getResources(), R.drawable.blossom))));
dataIterator = blossomVector.iterator();
blossomNum++;
Log.v(TAG, "Number of Blossoms =" + blossomNum);
}
};
private synchronized void DrawBlossoms(Canvas c) // method to draw flowers on screen and test for collision
{
Canvas canvas = c;
dataIterator = blossomVector.iterator();
while (dataIterator.hasNext())
{
Blossom tempBlossom = dataIterator.next();
tempBlossom.Draw(canvas);
if (tempBlossom.hit(box_x,box_y, box_x + boxWidth, box_y + boxHeight, blossomVector) == true)
{
Log.v(TAG, "ITERATOR WORKS!");
dataIterator.remove();
currentScore += 100;
}
if (tempBlossom.dropped() == true)
{
dataIterator.remove();
Log.v(TAG, "Blossom dropped");
lives--;
}
if (lives == 0)
{
// END THE GAME!!!
((BoardView) getParent()).findViewById(1).bringToFront();
}
}
}
public void writeFile()
{
}
}
public class BoardThread extends Thread {
private SurfaceHolder surfaceHolder;
private BoardView boardView;
private Vector<Blossom> blossomVector;
private int boxX;
private int boxY;
private int boxWidth;
private int boxHeight;
private boolean mrun =false;
private Iterator<Blossom> iterator;
private static final String TAG = "Debug";
public BoardThread(SurfaceHolder holder, BoardView boardView2,
Vector<Blossom> blossomVector1, Iterator<Blossom> dataIterator,
int box_x, int box_y, int boxW, int boxH) {
surfaceHolder = holder;
boardView=boardView2;
blossomVector = blossomVector1;
iterator = dataIterator;
boxX = box_x;
boxY = box_y;
boxW = boxWidth;
boxH = boxHeight;
}
public void startRunning(boolean run) {
mrun=run;
}
#Override
public void run() {
super.run();
Canvas canvas;
while (mrun) {
canvas=null;
try {
canvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
//update position
//Position(blossomVector, boxX, boxY, boxWidth, boxHeight);
// draw flowers
boardView.onDraw(canvas);
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
private synchronized void Position(Vector<Blossom> blossomVector,int box_x, int box_y,
int boxWidth, int boxHeight)
{
//for(Blossom blossom: blossomVector)
iterator = blossomVector.iterator();
while (iterator.hasNext())
{
Blossom tempBlossom = iterator.next();
tempBlossom.UpdatePosition();
}
}
}
public class Board extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//use a frame layout so you can also display a dialog box
// allows more than one view to be used
FrameLayout f1 = new FrameLayout(this);
LinearLayout l1 = new LinearLayout(this);
EditText edit = new EditText(this);
l1.setOrientation(LinearLayout.VERTICAL);
l1.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
edit.setText("Enter your name!");
l1.setId(1);
l1.addView(edit);
f1.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
f1.addView(l1);
f1.addView(new BoardView(this));
setContentView(f1);
//setContentView(new BoardView(this));
}
}
You need to run the bring to front on the right thread. Try this:
final View v = ((BoardView) getParent()).findViewById(1);
v.post(new Runnable(){ public void run(){ v.bringToFront(); } });
See if that works.
You must update the View using the UI thread (not the SurfaceView thread). If you would like to make some View change from within the SurfaceView thread you must create a new RunOnUiThread which performs the View updating.
// From within SurfaceView thread
new Thread() {
public void run() {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
// Update view here
}
});
}
}.start();