Flickering problems due to double buffer of SurfaceView

If you try the code in last post Link SurfaceView and Background Thread work together, you can note that the screen seem to flicker between two bitmap! It's due to double buffer of Android's SurfaceView: When you draw on Buffer A, Buffer B is being displayed; then you draw on Buffer B, Buffer A is being displayed. Such that you draw random points on Buffer A and B alternatively, not a single bitmap. This feature can solve some problem of display performance - but not in our case.

In order to solve the problem, you can:
- Draw each pixel on each buffer (It's not practical in our case), OR
- Draw on a single bitmap, then draw the bitmap on canvas.

Here is how to solve the double buffer problem using the second approach:

Modify the SurfaceView - MyGameSurfaceView.java

In surfaceCreated(), create a bitmap(myCanvasBitmap) accroading to the dimension of the SurfaceView. (Please note that not in MyGameSurfaceView_OnResume() - because the SurfaceView may be not yet ready when MyGameSurfaceView_OnResume() is called.) Create a new canvas(myCanvas). Then specify the bitmap(myCanvasBitmap) for the canvas(myCanvas) to draw into, by calling myCanvas.setBitmap(myCanvasBitmap) - Everything draw on myCanvas will draw on myCanvasBitmap.

In onDraw(), we draw on myCanvas, NOT canvas from the method argument. So everything will be draw on myCanvasBitmap. Then, draw the bitmap on canvas (from the method argument) using identity matrix, by calling canvas.drawBitmap(myCanvasBitmap, identityMatrix, null).

Such that, all we draw are draw on a single bitmap; to make both buffer consistance.

MyGameSurfaceView.java
package com.MyGame;

import java.util.Random;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
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;

int myCanvas_w, myCanvas_h;
Bitmap myCanvasBitmap = null;
Canvas myCanvas = null;
Matrix identityMatrix;

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) {

myCanvas_w = getWidth();
myCanvas_h = getHeight();
myCanvasBitmap = Bitmap.createBitmap(myCanvas_w, myCanvas_h, Bitmap.Config.ARGB_8888);
myCanvas = new Canvas();
myCanvas.setBitmap(myCanvasBitmap);

identityMatrix = new Matrix();
}

@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, 200);
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 = myCanvas.getWidth();
//int h = myCanvas.getHeight();
int x = random.nextInt(myCanvas_w-1);
int y = random.nextInt(myCanvas_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);
myCanvas.drawPoint(x, y, paint);

canvas.drawBitmap(myCanvasBitmap, identityMatrix, null);

}

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);
}
}
}

}



next:
- Create transparent foreground SurfaceView

0 Response to "Flickering problems due to double buffer of SurfaceView"

Posting Komentar