Selasa, 24 Januari 2012

Link SurfaceView and Background Thread work together

In previous posts "Create SurfaceView for our game" and "Implement background thread for our game", we have created DUMMY MyGameSurfaceView and MyGameThread classes, without any function, and without any interaction. In this step, we are going to modify the Java code to make them work together.



Modify main.xml to include MyGameSurfaceView in the layout.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<com.MyGame.MyGameSurfaceView
android:id="@+id/myview1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>


Modify the main activity (MyGameActivity), call myGameSurfaceView1.MyGameSurfaceView_OnResume() in onResume() and call myGameSurfaceView1.MyGameSurfaceView_OnPause() in onPause(); to start and stop the background thread.
package com.MyGame;

import android.app.Activity;
import android.os.Bundle;

public class MyGameActivity extends Activity {

MyGameSurfaceView myGameSurfaceView1;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myGameSurfaceView1 = (MyGameSurfaceView)findViewById(R.id.myview1);

}

@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
myGameSurfaceView1.MyGameSurfaceView_OnResume();
}

@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
myGameSurfaceView1.MyGameSurfaceView_OnPause();
}

}


Modify MyGameThread.java. It's a classic structure of backgraound thread. One thing have to note is the statement parent.updateSurfaceView() in run(), it will call back the function updateSurfaceView() in it's parent object.
package com.MyGame;

public class MyGameThread extends Thread {

volatile boolean running = false;

MyGameSurfaceView parent;
long sleepTime;

MyGameThread(MyGameSurfaceView sv, long st){
super();
parent = sv;
sleepTime = st;
}

public void setRunning(boolean r){
running = r;
}

@Override
public void run() {
// TODO Auto-generated method stub
while(running){

try {
sleep(sleepTime);
parent.updateSurfaceView();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}

}


Modify MyGameSurfaceView.java. It's the main part in the code. updateSurfaceView() is a call back function from background thread (MyGameThread.java) in pre-defined duration reached. In-directly, it call updateStates() and onDraw(canvas) to update states (not yet implemented) and draw something on screen (some random drawing is implemented). Please note that updateSurfaceView() is called from background thread, that means it run in background thread, not in UI thread. That's why we need something like surfaceHolder.lockCanvas(), surfaceHolder.unlockCanvasAndPost(canvas) and synchronized (surfaceHolder).
package com.MyGame;

import java.util.Random;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MyGameSurfaceView extends SurfaceView implements SurfaceHolder.Callback{

SurfaceHolder surfaceHolder;

MyGameThread myGameThread = null;

private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Random random;

public MyGameSurfaceView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}

public MyGameSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}

public MyGameSurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub

}

@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub

}

public void MyGameSurfaceView_OnResume(){

random = new Random();
surfaceHolder = getHolder();
getHolder().addCallback(this);

//Create and start background Thread
myGameThread = new MyGameThread(this, 500);
myGameThread.setRunning(true);
myGameThread.start();

}

public void MyGameSurfaceView_OnPause(){
//Kill the background Thread
boolean retry = true;
myGameThread.setRunning(false);

while(retry){
try {
myGameThread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

@Override
protected void onDraw(Canvas canvas) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);

int w = canvas.getWidth();
int h = canvas.getHeight();
int x = random.nextInt(w-1);
int y = random.nextInt(h-1);
int r = random.nextInt(255);
int g = random.nextInt(255);
int b = random.nextInt(255);

paint.setColor(0xff000000 + (r << 16) + (g << 8) + b);
canvas.drawPoint(x, y, paint);
}

public void updateStates(){
//Dummy method() to handle the States
}

public void updateSurfaceView(){
//The function run in background thread, not ui thread.

Canvas canvas = null;

try{
canvas = surfaceHolder.lockCanvas();

synchronized (surfaceHolder) {
updateStates();
onDraw(canvas);
}
}finally{
if(canvas != null){
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}

}


The idea is:
  • The main.xml layout define MyGameSurfaceView.
  • MyGameSurfaceView start background thread.
  • When background thread reach the pre-defined duration, it call back updateSurfaceView() function of
  • MyGameSurfaceView to do and draw something.


Next:
- Flickering problems due to double buffer of SurfaceView

0 komentar:

Posting Komentar

Copyright © 2012 Codding News All Right Reserved