You are new to this series? Please start with the first part.

The second part of this series will show what you have to change to switch from using the View class to SurfaceView class.

Just to remember, our last code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.droidnova.android.tutorial2d;
 
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
 
public class Tutorial2D extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(new Panel(this));
    }
 
    class Panel extends View {
        public Panel(Context context) {
            super(context);
        }
 
        @Override
        public void onDraw(Canvas canvas) {
            Bitmap _scratch = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
            canvas.drawColor(Color.BLACK);
            canvas.drawBitmap(_scratch, 10, 10, null);
        }
    }
}


Now lets simply change the super class of our Panel from View to SurfaceView.

1
2
3
// code snipped
class Panel extends SurfaceView {
// code snipped

Lets compile and start and you will see…. nothing.
The reason therefor is, that we need the SurfaceHolder, which provide us with the canvas we can draw on.

So one more change: use SurfaceHolder.Callback interface. The interface requires 3 additional methods we need to implement: surfaceCreated(), surfaceDestroyed(), surfaceChanged()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// code snipped
class Panel extends SurfaceView implements SurfaceHolder.Callback {
    @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
    }
}

Additional we have to add the Panel to the SurfaceHolder for a callback. We will do this in the Panel constructor.

1
2
3
4
public Panel(Context context) {
    super(context);
    getHolder().addCallback(this);
}

The next big thing: We need a Thread which will control the drawings. Lets create the inner class TutorialThread which extends from Thread class. We need a constructor with the Panel and the SurfaceHolder as parameters, a setter to set the variable which keeps the thread running and we need to override the method run().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class TutorialThread extends Thread {
    private SurfaceHolder _surfaceHolder;
    private Panel _panel;
    private boolean _run = false;
 
    public TutorialThread(SurfaceHolder surfaceHolder, Panel panel) {
        _surfaceHolder = surfaceHolder;
        _panel = panel;
    }
 
    public void setRunning(boolean run) {
        _run = run;
    }
 
    @Override
    public void run() {
 
    }
}

Now lets use the TutorialThread in our Panel:

1
2
3
4
5
6
7
8
9
class Panel extends SurfaceView implements SurfaceHolder.Callback {
    private TutorialThread _thread;
 
    public Panel(Context context) {
        super(context);
        getHolder().addCallback(this);
        _thread = new TutorialThread(getHolder(), this);
    }
    // code snipped

We can also implement the methods surfaceCreated() and surfaceDestroyed(). The method surfaceCreated() after the surface was created. So we can set our running variable to true and start the thread.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
public void surfaceCreated(SurfaceHolder holder) {
    _thread.setRunning(true);
    _thread.start();
}
 
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    // simply copied from sample application LunarLander:
    // we have to tell thread to shut down & wait for it to finish, or else
    // it might touch the Surface after we return and explode
    boolean retry = true;
    _thread.setRunning(false);
    while (retry) {
        try {
            _thread.join();
            retry = false;
        } catch (InterruptedException e) {
            // we will try it again and again...
        }
    }
}

Now we are near the finish line because we just have to implement the run() method.
First of all we need a Canvas object (line 3). Then a while loop with our _run variable (line 4)
Inside the loop we set our Canvas object to null and open a try block (line 5-6).
Inside this try block, we have to lock the canvas, draw on it, unlock and post it.
We are inside of a thread, so we have to synchronize at least our drawing with the SurfaceHolder (line 8-10).
We unlock and post it in the finally block to make sure, we don’t leave the Surface in an inconsistent state.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public void run() {
    Canvas c;
    while (_run) {
        c = null;
        try {
            c = _surfaceHolder.lockCanvas(null);
            synchronized (_surfaceHolder) {
                _panel.onDraw(c);
            }
        } finally {
            // do this in a finally so that if an exception is thrown
            // during the above, we don't leave the Surface in an
            // inconsistent state
            if (c != null) {
                _surfaceHolder.unlockCanvasAndPost(c);
            }
        }
    }
}

Finally we should have this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package com.droidnova.android.tutorial2d;
 
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
 
public class Tutorial2D extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(new Panel(this));
    }
 
    class Panel extends SurfaceView implements SurfaceHolder.Callback {
        private TutorialThread _thread;
 
        public Panel(Context context) {
            super(context);
            getHolder().addCallback(this);
            _thread = new TutorialThread(getHolder(), this);
        }
 
        @Override
        public void onDraw(Canvas canvas) {
            Bitmap _scratch = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
            canvas.drawColor(Color.BLACK);
            canvas.drawBitmap(_scratch, 10, 10, null);
        }
 
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            // TODO Auto-generated method stub
 
        }
 
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            _thread.setRunning(true);
            _thread.start();
        }
 
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            // simply copied from sample application LunarLander:
            // we have to tell thread to shut down & wait for it to finish, or else
            // it might touch the Surface after we return and explode
            boolean retry = true;
            _thread.setRunning(false);
            while (retry) {
                try {
                    _thread.join();
                    retry = false;
                } catch (InterruptedException e) {
                    // we will try it again and again...
                }
            }
        }
    }
 
    class TutorialThread extends Thread {
        private SurfaceHolder _surfaceHolder;
        private Panel _panel;
        private boolean _run = false;
 
        public TutorialThread(SurfaceHolder surfaceHolder, Panel panel) {
            _surfaceHolder = surfaceHolder;
            _panel = panel;
        }
 
        public void setRunning(boolean run) {
            _run = run;
        }
 
        @Override
        public void run() {
            Canvas c;
            while (_run) {
                c = null;
                try {
                    c = _surfaceHolder.lockCanvas(null);
                    synchronized (_surfaceHolder) {
                        _panel.onDraw(c);
                    }
                } finally {
                    // do this in a finally so that if an exception is thrown
                    // during the above, we don't leave the Surface in an
                    // inconsistent state
                    if (c != null) {
                        _surfaceHolder.unlockCanvasAndPost(c);
                    }
                }
            }
        }
    }
}

Screenshot which shows the same icon as in part I.
Simple graphic with Android

Go to Playing with graphics in Android – Part III

Share