I have a SurfaceView in an Activity, and I want to open up a new Activity when something occurs in the SurfaceView(when you run out of lives - lives == 0). I've tried different things, but I keep having problems with it. If I stop my UIThread first, then of course it won't keep running and so won't be able to execute the startActivity statement. If I do start the activity, it freezes up on me and force closes - having to do with my UIThread I believe. Has anybody run into this problem before - and if so, do you have any idea how I might go about achieving this. At the very least, if I can't open up a new Activity, how could I CLOSE this current Activity (from inside the SurfaceView).
public class BoardView extends SurfaceView implements SurfaceHolder.Callback{
Context mContext;
// thread initialization
private BoardThread thread;
Thread timer;
Thread timer2;
// 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);
//set up read/write data
File root = Environment.getExternalStorageDirectory();
File highscoresFile = new File(root, "highscores.txt");
// controls drawings
thread = new BoardThread(getHolder(),this, blossomVector, dataIterator, box_x, box_y,
boxWidth, boxHeight);
timer2 = new Thread(){
public void run(){
while(game == false){
uiCallback.sendEmptyMessage(0);
try{
Thread.sleep(5000); // change to be random
}
catch (InterruptedException e){
e.printStackTrace();
}
}
}
};
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();
timer2.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");
try {
thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
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)
{
// stop the thread that makes blossoms
game = true;
//save the highscore
try {
File root = Environment.getExternalStorageDirectory();
if(root.canWrite()){
File highscoresFile = new File(root, "highscores.txt");
FileWriter writer = new FileWriter(highscoresFile);
BufferedWriter out = new BufferedWriter(writer);
//out.newLine();
out.write(score);
out.close();
}
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
File root = Environment.getExternalStorageDirectory();
File highscoresFile = new File(root, "highscores.txt");
FileReader reader = new FileReader(highscoresFile);
BufferedReader in = new BufferedReader(reader);
try {
String scoreTest = in.readLine();
Log.v(TAG, "Score: " + scoreTest);
reader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
Board Thread`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();
}
}
}
`
Sounds like the SurfaceView is running in a different thread than the main UI thread and can't modify the View objects. You'll want to create a Handler in your activity and make it accessible to your SurfaceView. Then you can send a message to the handler and it can update the UI (or launch a new activity) in the main UI thread.
Related
I'm new to Android programming and i'm trying to understand the control flow in android programs. I've been working on a program to log and display sensor data.
public class MainActivity extends ActionBarActivity implements SensorEventListener
This is the main class with onCreate(), btnStart.setOnClickListener(new OnClickListener(), btnStop.setOnClickListener(new OnClickListener(), onSensorChanged(SensorEvent event) which do most of the important tasks.
onCreate() has all this code:
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
title = (TextView)findViewById(R.id.titleView);
acc_x = (TextView)findViewById(R.id.acc_x_values);
acc_y = (TextView)findViewById(R.id.acc_y_values);
acc_z = (TextView)findViewById(R.id.acc_z_values);
x = (TextView)findViewById(R.id.acc_x);
y = (TextView)findViewById(R.id.acc_y);
z = (TextView)findViewById(R.id.acc_z);
btnStart = (Button)findViewById(R.id.button_start);
btnStop = (Button)findViewById(R.id.button_stop);
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mAccel = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(this,sensorManager.getDefaultSensor(sensorType), 20000);
And the start and stop button funtionalities are defined here.
btnStart.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
startFlag = true;
String storepath = Environment.getExternalStorageDirectory().getPath();
System.out.println("Stored at" +storepath); // /storage/sdcard
Toast.makeText(getBaseContext(), "Started Recording Data and Storing at"+storepath, Toast.LENGTH_SHORT).show();
try {
myFile = new File(storepath + "/GaitApp/" + name.getText() + "_acc.csv");
myFile.createNewFile();
fOut = new FileOutputStream(myFile);
myOutWriter = new OutputStreamWriter(fOut);
myBufferedWriter = new BufferedWriter(myOutWriter);
myPrintWriter = new PrintWriter(myBufferedWriter);
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
} //onclick
}); //startbutton
btnStop.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Toast.makeText(getBaseContext(), "Stopped Recording",Toast.LENGTH_SHORT).show();
startFlag = false;
try {
fOut.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}// onClick
}); // btnstopButton
onSensorChanged() is:
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
if(startFlag){
float[] values = event.values;
// Movement
float x_float = values[0];
float y_float = values[1];
float z_float = values[2];
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String currentDateandTime = sdf.format(new Date());
long curTime = System.currentTimeMillis();
if ((curTime - lastUpdate) > 20) {
long diffTime = (curTime - lastUpdate);
lastUpdate = curTime;
x_float = event.values[0];
y_float = event.values[1];
z_float = event.values[2];
acc_x.setText(Float.toString(x_float));
acc_y.setText(Float.toString(y_float));
acc_z.setText(Float.toString(z_float));
String res=String.valueOf(currentDateandTime+", "+x_float)+", "+String.valueOf(y_float)+", "+String.valueOf(z_float+"\n");
Log.d("test", res);
for (int i = 0; i % 1 == 0; i++) {
if (startFlag) {
try{
fOut = new FileOutputStream(myFile);
myOutWriter = new OutputStreamWriter(fOut);
myBufferedWriter = new BufferedWriter(myOutWriter);
myPrintWriter = new PrintWriter(myBufferedWriter);
myPrintWriter.append(curTime - lastUpdate + ", " + x_float + ", " + y_float + ", " + z_float+ "\n");
}
catch (IOException e){
System.out.println("Exception: "+e);
}
//myPrintWriter.write(curTime - lastUpdate + ", " + x_float + ", " + y_float + ", " + z_float+ "\n");
} //startFlag is true
else {
try {
myOutWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fOut.close();
} catch (IOException e) {
e.printStackTrace();
} // catch
} // else
} //for
} // if -- time
} // startFlag
} //accelerometer -- sensor
} // onSensorChanged
I'm trying to understand how the onSensorChanged() transfers control and in the process the acceleration data to onCreate() to be displayed and stored.
The onCreate method is only used to initially setup the view. Updates to the display must occur by deliberately invoking them. Furthermore updates to the UI must occur on the UI thread. Since the onSensorChanged call can occur asynchronously, if you wish sensor updates to trigger a display update you must send yourself a UI thread event. One option is to use a handler such as in the following untested code.
private Handler mHandler;
public void onCreate() {
mHandler = new Handler();
}
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
saveData(event);
final long currentTime = System.currentTimeMillis();
if (isResumed() && currentTime > (lasttime + SOME_DELAY)) {
lasttime = currentTime;
handler.post(new Runnable() {
#Override
public void run() {
updateDisplay(event);
}
});
}
}
}
private void updateDisplay(SensorEvent event) {
acc_x.setText(...);
acc_y.setText(...);
acc_z.setText(...);
x.setText(...);
y.setText(...);
z.setText(...);
}
Depending on your application you may wish to log your data in a service.
onSensorChanged() is the handler in this case. This call back function takes care of updating the UI and writing to the file.
I calculate decibels using the formula double dB = 20 * Math.log10(p/p0);
p = recorder.getMaxAmplitude()/51805.5336;
p0 = 0.0002;
But what this class does is representing the sound amplitude in a scale between 40 to 70, and it goes to 70 (maximum value) very easily, just tapping my fingers on the device.
final Runnable updater = new Runnable(){
public void run(){
updateTv();
};
};
final Handler mHandler = new Handler();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tv = (TextView)findViewById(R.id.textView1);
tv.setText("Start");
startRecording();
if (runner == null)
{
runner = new Thread(){
public void run()
{
while (runner != null)
{
try
{
Thread.sleep(500);
Log.i("Noise", "Tock");
} catch (InterruptedException e) { };
mHandler.post(updater);
}
}
};
runner.start();
Log.d("Noise", "start runner()");
}
}
public void onResume()
{
super.onResume();
}
protected void updateTv() {
double decibel = Math.rint(100*getAmplitudeEMA())/100;
tv.setText("" + decibel + " dB");
AppLog.logString(Double.toString((getAmplitudeEMA())) + " dB");
}
private double getAmplitudeEMA() {
double p = getAmplitude()/51805.5336;
double p0 = 0.0002;
//mEMA = EMA_FILTER * amp + (1.0 - EMA_FILTER) * mEMA;
double mEMA2 = 20 * Math.log10(p/p0);
Log.i("MY", "" + recorder.getMaxAmplitude());
return mEMA2;
}
private double getAmplitude() {
if(recorder != null){
return recorder.getMaxAmplitude(); }
else
return 0;
}
private String getFilename(){
String filepath = Environment.getExternalStorageDirectory().getPath();
File file = new File(filepath,AUDIO_RECORDER_FOLDER);
AppLog.logString(filepath);
if(!file.exists()){
file.mkdirs();
}
AppLog.logString(file.getAbsolutePath() + "/" + System.currentTimeMillis() + file_exts[currentFormat]);
return (file.getAbsolutePath() + "/" + System.currentTimeMillis() + file_exts[currentFormat]);
}
private void startRecording(){
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(output_formats[currentFormat]);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(getFilename());
recorder.setOnErrorListener(errorListener);
recorder.setOnInfoListener(infoListener);
try {
recorder.prepare();
recorder.start();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
I am new to android Surfaceview implementation. I am developing RDP client application in android3.0. I getting the image stream from socket, drawing this image to surface using Surfaceview and Thread.
The sample code of surface view:
class mySurfaceView extends SurfaceView implements SurfaceHolder.Callback
{
private TutorialThread _thread;
Canvas c=null;
public mySurfaceView(Context context)
{
super(context);
getHolder().addCallback(this);
_thread = new TutorialThread(getHolder(), this);
matrix= new Matrix();
m = new float[9];
paint = new Paint();
}
public void surfaceCreated(SurfaceHolder arg0) {
//setWillNotDraw(false) ;
Log.e("surfaceCreated","surfaceCreated");
if(_thread==null ){
Log.e("_thread.created","thread created");
_thread = new TutorialThread(getHolder(),this);
_thread.setRunning(true);
_thread.start();
// <-- added fix
}else {
Log.e("_thread.getState()",_thread.getState()+"");
_thread.setRunning(true); //original code
_thread.start(); //original code
}
}
public void surfaceDestroyed(SurfaceHolder arg0) {
Log.e("surfaceDestroyed","surfaceDestroyed");
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,
int arg3) {
}
class TutorialThread extends Thread
{
private SurfaceHolder _surfaceHolder;
private mySurfaceView _panel;
private boolean _run = false;
private InputStream is;
private Socket socket;
Bitmap resizeBmp;
public TutorialThread(SurfaceHolder surfaceHolder,mySurfaceView panel)
{
Log.e("TutorialThread","TutorialThread-->Constructor");
_surfaceHolder = surfaceHolder;
_panel = panel;
}
public void setRunning(boolean run) {
_run = run;
}
#Override
public void run()
{
Log.e("TutorialThread","TutorialThread-->run()");
try
{
socket = new Socket("172.19.1.144", 4444);
is = socket.getInputStream();
DataInputStream inputputStream = new DataInputStream(is);
//long time = System.currentTimeMillis();
int i=0;
while (socket.isConnected() )
{
Log.e("tutorial thread","While running");
c = null;
i++;
if(i==10){
System.gc();
i=0;
}
Log.e("BEFORE","BEFORE");
synchronized (_surfaceHolder)
{
Log.e("AFTER","AFTER");
ByteBuffer inputHeaderBuffer = ByteBuffer.allocate(20);
inputHeaderBuffer.order(ByteOrder.LITTLE_ENDIAN);
inputputStream.readFully(inputHeaderBuffer.array());
SurfaceViewPinchZoom.serverWidth=inputHeaderBuffer.getInt();
SurfaceViewPinchZoom.serverHeight=inputHeaderBuffer.getInt();
//Log.e("serverWidth","serverWidth "+ SurfaceViewPinchZoom.serverWidth+"serverHeight===="+SurfaceViewPinchZoom.serverHeight);
SurfaceViewPinchZoom.left=inputHeaderBuffer.getInt();
SurfaceViewPinchZoom.top=inputHeaderBuffer.getInt();
int dataLength = inputHeaderBuffer.getInt();
ByteBuffer imageDataCompress = ByteBuffer.allocate(dataLength);
imageDataCompress.order(ByteOrder.LITTLE_ENDIAN);
inputputStream.readFully(imageDataCompress.array());
byte[] imagedata = new byte[imageDataCompress.remaining()];
imageDataCompress.get(imagedata);
//Decompress the image
//Log.e("imagedata.length::::::::::",imagedata.length+"");
// Create the decompressor and give it the data to compress
Inflater decompressor = new Inflater();
decompressor.setInput(imagedata);
// Create an expandable byte array to hold the decompressed data
ByteArrayOutputStream bos = new ByteArrayOutputStream(imagedata.length);
// Decompress the data
byte[] buf = new byte[1024];
while (!decompressor.finished()) {
try {
int count = decompressor.inflate(buf);
bos.write(buf, 0, count);
} catch (DataFormatException e) {
}
}
try {
bos.close();
} catch (IOException e) {
}
// Get the decompressed data
byte[] decompressedData = bos.toByteArray();
/
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
// Log.e("decompressedData.length::::::::::",decompressedData.length+"");
/*SurfaceViewPinchZoom.*/bmp = BitmapFactory.decodeByteArray(decompressedData, 0,decompressedData.length,options);
options.inDither=true;
/*scaleX=(float)screen_width/bmp.getWidth();
scaleY=(float)screen_height/bmp.getHeight();
matrix.setScale(scaleX, scaleY);*/
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, screen_width, screen_height);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
/*SurfaceViewPinchZoom.*/bmp= BitmapFactory.decodeByteArray(decompressedData, 0,decompressedData.length,options);
bmp=BitmapFactory.decodeByteArray(decompressedData, 0,decompressedData.length,options);
c = _surfaceHolder.lockCanvas();
c.drawBitmap(bmp, matrix, paint);
//draw(c);
//postInvalidate();
onDraw(c);
inputHeaderBuffer.clear();
imageDataCompress.clear();
inputHeaderBuffer = null;
imageDataCompress = null;
imagedata = null;
}
if (c != null)
{
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
catch (ArrayIndexOutOfBoundsException ae)
{
ae.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
finally {
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
private int calculateInSampleSize(Options options, int screen_width,
int screen_height) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > screen_height || width > screen_width) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)screen_height);
} else {
inSampleSize = Math.round((float)width / (float)screen_width);
}
}
return inSampleSize;
}
}
}
The problems are
1) if I press home button then surfaceDestroyed() is called and thread is terminated.
But I need to continue the Thread and update the images(input stream from socket) after open the application from home button is pressed.
2) if I call the activity on doubletap event as child activity, surfaceDestroyed() is called and Thread is terminated.
Where I need to continue the images display after returning from child activity.
In both the case I am getting exception as java.lang.IllegalThreadStateException: Thread already started.
Could please help how to run the same thread without terminating on home button is pressed or another child activity is called?
Thanks & Regards
Yamini
I'm using a SurfaceView and I have to update it as fast as I can. Since the onDraw method isn't called automatically I have to call explicitly. So my first attempt was on threads, but I got some lag. There is a better way to do this? If there is a better solution, can you, please, explain me? I'm new on Android's World!
What I have so far
public void run() {
while(running){
Canvas c = null;
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
}
}
Here's my code that I use as a rendering thread. The fps is updated frame dependent. I usually run my games at 30 fps.
(There's some code you won't need, but I'm sire you'll be able to find the pieces you are looking for.)
public class RenderView extends SurfaceView implements Runnable {
Game game;
Bitmap gameScreen;
Thread gameloop;
SurfaceHolder holder;
boolean running;
int sleepTime;
int numberOfFramesSkipped;
int maxFrameSkips;
long beginTime;
long endTime;
long lastTime;
int differenceTime;
int framePeriod;
Canvas frameBuffer;
int frameCount;
public RenderView(int fps, int maxFrameSkips) {
super(game);
this.gameScreen = gameScreen;
this.holder = getHolder();
this.framePeriod = 1000/fps;
this.maxFrameSkips = maxFrameSkips;
lastTime = System.currentTimeMillis();
beginTime = System.currentTimeMillis();
}
#Override
public void run() {
while(running == true) {
if(holder.getSurface().isValid()) {
beginTime = System.currentTimeMillis();
// call your update method here
// render here
frameBuffer = holder.lockCanvas();
this.onDraw();
holder.unlockCanvasAndPost(frameBuffer);
// Frame Per Second Count
frameCount++;
if(lastTime + 1000 < System.currentTimeMillis()) {
lastTime = System.currentTimeMillis();
frameCount = 0;
}
endTime = System.currentTimeMillis();;
differenceTime = (int) (endTime - beginTime);
sleepTime = (int) (framePeriod - differenceTime);
if(sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
else {
while(sleepTime < 0 && numberOfFramesSkipped < this.maxFrameSkips) {
// Call your update method here
sleepTime += framePeriod;
numberOfFramesSkipped++;
}
}
}
}
}
private void renderFrameBuffer() {
// Update the current virtual screen image
game.getCurrentScreen().render();
// Render the current virtual screen to the real phone screen
frameBuffer = holder.lockCanvas();
if(frameBuffer != null) { // Fix for mysterious bug ( FATAL EXCEPTION: Thread)
// The viewing area of our virtual screen on our real screen
// Composition
// First layer (bottom)
frameBuffer.drawBitmap(this.gameScreen, null, game.getWSScreen().getGameScreendst(), null);
// Second layer (top)
frameBuffer.drawBitmap(this.gameScreenExtended, null, game.getWSScreen().getGameScreenextendeddst(), null);
holder.unlockCanvasAndPost(frameBuffer);
}
else {
gameEngineLog.e(classTAG, "Surface has not been created or otherwise cannot be edited");
}
}
public void resume() {
this.running = true;
gameloop = new Thread(this);
gameloop.start();
}
public void pause() {
this.running = false;
running = false;
while(true) {
try {
gameloop.join();
break;
} catch (InterruptedException e) {
// retry
}
}
}
}
public void onDraw() {
}
I suggest you take a look at this link or have a look at
http://code.google.com/p/beginning-android-games/
or http://www.edu4java.com/en/androidgame/androidgame3.html
I've been trying to figure this out for a while now... I need to place marks over top of a seekBar to show the user places that they bookmarked in the past. The data is stored in xml. The problem is making the little ovals appear over the seekBar... It just doesn't work...
Here's my code:
public class seekMark extends View {
private int seekLength; // in pixels
private int seekLeftPad; // in pixels
private int seekBottomPad; // in pixels
private int trackLength; // in ms
private float pxOverMs; // in px/ms
ShapeDrawable lmark;
private seekMark instance;
public seekMark(Context context){
super(context);
instance = this;
seekLength = progressBar.getWidth();
seekLeftPad = progressBar.getPaddingLeft();
seekBottomPad = progressBar.getBottom();
trackLength = player.getDuration();
pxOverMs = pxPerMs();
lmark = new ShapeDrawable(new OvalShape());
}
private float pxPerMs(){
return ((float) seekLength)/((float) trackLength);
}
private int[] markPxList() throws XmlPullParserException, IOException {
int bmStartTime = 0;
String bmNames[] = bmNameList(xmlPath);
int[] bmPos = new int[bmNames.length];
for(int i=0; i < bmNames.length; i++){
bmStartTime = getBookmark(xmlPath, bmNames[i]);
bmPos[i] = (int) (bmStartTime * pxOverMs);
}
return (bmPos);
}
public void markPlace() throws XmlPullParserException, IOException {
int y = seekBottomPad;
int x = 0;
int bmPos[] = markPxList();
for(int i = 0; i < bmPos.length; i++){
x = bmPos[i] + seekLeftPad;
lmark = new ShapeDrawable();
lmark.getPaint().setColor(0xff74AC23);
lmark.setBounds(x, y, x + 1, y + 1);
instance.invalidate();
}
}
protected void onDraw(Canvas canvas) {
lmark.draw(canvas);
}
}
It's called from onCreate using this code. I call it using in another thread to avoid the problem where the dimensions of progressBar aren't yet set in onCreate.
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
if (display.getRotation() == 1){ // if landscape
final Runnable runner = new Runnable() {
public void run() {
seekMark seekMarks = new seekMark(context);
try {
seekMarks.markPlace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// runs in another thread to avoid the problem with calling
// seekMark directly from onCreate
}
};
handler.postDelayed(runner, 1000);
}
The program crashes whenever I try to call seekMark.markPlace()... I'm trying to draw this over top of my layout main.xml.
im not sure if this is what you are trying to do.
Customize Seekbar
this seems to be similar while the approach is different.