Android SurfaceView Synchronous Creation

2

I created a subclass of SurfaceView to abstract canvas operations, such as drawBitmap (among others), as shown below:

public class MyView extends SurfaceView {

    public MyView(Context c) {
       super(c);
       this.canvasHolder = this.getHolder();
       this.canvasHolder.addCallback(surfaceHolderCallback);
    }

    public void drawBitmap(Bitmap bitmap, float width, float height, Paint p) {
        canvas.drawBitmap(bitmap, width, height, p);
    }

    private SurfaceHolder.Callback surfaceHolderCallback = new SurfaceHolder.Callback() {
       @Override
       public void surfaceCreated(SurfaceHolder holder) {
           canvas = holder.lockCanvas();
       }

       @Override
       public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}

       @Override
       public void surfaceDestroyed(SurfaceHolder holder) {}
    };

    public void flush() {
       this.canvasHolder.unlockCanvasAndPost(this.canvas);
       this.canvas = this.canvasHolder.lockCanvas();
    }

The full initialization of SurfaceView depends on the callback, in which I get access to the canvas I need to manipulate. Otherwise, I get a NullpointerException on the first drawBitmap call.

The problem is that I want to allow the use of the class quite directly, as shown below. To do this, I need to abstract all of this initialization mechanism with callback and ensure that the drawBitmap has access to a valid canvas (not null).

Any ideas how to do this?

    MyView view = new MyView(getBaseContext());
    String source = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/abc.png";
    view.drawBitmap(BitmapFactory.decodeFile(source).copy(Bitmap.Config.ARGB_8888, true), 0, 0, null);
    view.flush();
    
asked by anonymous 10.12.2015 / 15:05

1 answer

0

If the "valid state" of an instance of the MyView class depends on a call to a callback it is as if the "instance creation was asynchronous" >

I do not see how you can ensure that the instance is not used before it has a "valid state."

However, it is possible to provide the user with a place to use it with this guarantee.

One way is to pass a callback on the constructor and call it after the canvas is valid.

It would be something like this:

public class MyView extends SurfaceView {

    //Iterface que o callback tem de implemantar
    public interface onCreatedCallback {
        void onCreated(MyView myView);
    }

    private final SurfaceHolder canvasHolder;
    private Canvas canvas;
    private onCreatedCallback onCreatedCallback;

    public MyView(Context c, onCreatedCallback callback) {
        super(c);
        //Guarda o callback
        onCreatedCallback = callback;
        this.canvasHolder = this.getHolder();
        this.canvasHolder.addCallback(surfaceHolderCallback);
    }

    public void drawBitmap(Bitmap bitmap, float width, float height, Paint p) {
        canvas.drawBitmap(bitmap, width, height, p);
    }

    private SurfaceHolder.Callback surfaceHolderCallback = new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            canvas = holder.lockCanvas();

            //Nesta altura o canvas é valido
            if(onCreatedCallback != null){
                //Chama o callback
                onCreatedCallback.onCreated(MyView.this);
            }
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {}
    };

    public void flush() {
        this.canvasHolder.unlockCanvasAndPost(this.canvas);
        this.canvas = this.canvasHolder.lockCanvas();
    }
}  

The usage would look like this:

final String source = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/abc.png";
new MyView(getBaseContext(), new MyView.onCreatedCallback() {
    @Override
    public void onCreated(MyView myView) {
        myView.drawBitmap(BitmapFactory.decodeFile(source).copy(Bitmap.Config.ARGB_8888, true), 0, 0, null);
        myView.flush();
    }
});
    
10.12.2015 / 19:16