I wish to find out what dimensions my canvas is going to be long before I ever need to create one or draw on it.
The only canvas code I know (or have a flimsy knowledge of) is this:
final SurfaceHolder holder = getHolder();
try
{
Canvas canvas = holder.lockCanvas();
if(canvas != null)
{
onDraw(canvas);
holder.unlockCanvasAndPost(canvas);
}
}
catch (Exception e)
{
e.printStackTrace();
}
But this looks like it is doing far too much for simply getting the height. Is there a function like WhatHeightWillMyCanvasBeWhenIMakeOne()?
EDIT: ...and if there isn't such a function, then what is a minimal piece of code for temporarily getting a canvas for long enough to ask its height and then get rid of it (if needed).
You cannot get a canvas sized to your drawable-screen-area until you override the onDraw and do some canvas locking and posting. (from what I understand anyways)
#Override
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);
}
}
try {
sleep(10);
} catch (Exception e) {}
}
}
Now when you are calling on draw in your loop, you are sending it a canvas. now just implement the onDraw method in your custom view.
#Override
protected void onDraw(Canvas canvas) {
int h = canvas.getHeight();
int w = canvas.getWidth();
}
This can also be done w/o a canvas. first of all declare a class with some static holders.
public class Window {
public static int WINDOW_HEIGHT; // = to screen size
public static int WINDOW_WIDTH; // = to screen size
}
Next call some special features in your main start up activity. we request these because i've never seen anyone want to draw to a canvas and not want a full screen with no bar. If you plan on not using a full screen ignore the options. So to get an accurate measure we need to hide them here as well.
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
setContentView(R.layout.main); // <--- or here you can call your custom view
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
Window.WINDOW_HEIGHT = height;
Window.WINDOW_WIDTH = width;
}
from inside your custom view you could make a call like this
private void CreateWindow(Context context) {
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
Window.WINDOW_HEIGHT = height; // = to screen size
Window.WINDOW_WIDTH = width; // = to screen size
}
Related
I'm trying to use SurfaceView with Canvas to draw a waveform. I'm using one SurfaceView with Canvas to draw, and it works.
But when I want to make my first SurfaceView overlay by the second SurfaceView(using FrameLayout). It's doesn't work.
And these two questions appears to me:
1. If I use Canvas in second SurfaceView, then the second canvas becomes null;
2. If I don't use Canvas in second SurfaceView, but just call overlay, then the SurfaceView size will be overlay but the graph is same.
Referenced code is as below:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("onCreate", "here");
l1 = (LinearLayout) findViewById(R.id.l1);
l2 = (LinearLayout) findViewById(R.id.l2);
l2.setVisibility(View.INVISIBLE);
sfv = (SurfaceView) findViewById(R.id.sfv);
sfh = sfv.getHolder();
sfv.getHolder().setFormat(PixelFormat.TRANSLUCENT);
sfv2 = (SurfaceView) findViewById(R.id.sfv2);
sfh2 = sfv2.getHolder();
sfv2.getHolder().setFormat(PixelFormat.TRANSLUCENT);
paint.setColor(Color.BLUE);
paint.setStrokeWidth(3);
paint2.setColor(Color.RED);
paint2.setStrokeWidth(3);
Log.i("flow", "now at before init()");
tv = (TextView) findViewById(R.id.tv);
sfh.addCallback(this);
sfh2.addCallback(this);
//init();
}
public void init() {
Log.e("init", "here");
if (pic) {
canvas = sfh.lockCanvas(new Rect(xtime, 0, xtime + 2, getWindowManager().getDefaultDisplay().getHeight()));
canvas.drawARGB(255, 0, 0, 0);
for (int i = 0; i < 600; i++) {
a = 600 - i;
canvas.drawLine(xtime, oldy, xtime + 2, a, paint);
xtime += 2;
oldy = a;
if (xtime > 1000) {
xtime = 0;
oldy = 0;
}
}
sfh.unlockCanvasAndPost(canvas);
// tv.setText("in the init2");
}
else{
Log.e("sfh2", "here");
canvas = sfh2.lockCanvas(new Rect(xtime2, 0, xtime2 + 2, getWindowManager().getDefaultDisplay().getHeight()));
if(canvas == null){
Log.e("canvas", "null here");
}
else {
canvas.drawARGB(255, 255, 255, 255);
for (int i = 0; i < 300; i++) {
a = 300 - i;
canvas.drawLine(xtime2, oldy, xtime2 + 2, a, paint2);
xtime2 += 2;
oldy = a;
if (xtime2 > 1000) {
xtime2 = 0;
oldy2 = 0;
}
}
sfh2.unlockCanvasAndPost(canvas);
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Log.e("surfaceCreated", "here");
init();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.e("surfaceChanged", "here");
init();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.e("surfaceDestroyed", "here");
}
public void onchange(View view) {
if (pic) {
pic = false;
l1.setVisibility(View.VISIBLE);
l2.setVisibility(View.INVISIBLE);
tv.setText("change sf2");
Log.e("pic", "pic " + pic);
} else {
pic = true;
l1.setVisibility(View.INVISIBLE);
l2.setVisibility(View.VISIBLE);
tv.setText("change sf1");
Log.e("pic", "pic " + pic);
}
}
I have been in confusion for two weeks, hope somebody can help me to resolve this issue.
Thanks everyone.
First and foremost, bear in mind that a SurfaceView has two parts, the Surface and the View. The View part acts like any other View. The Surface is created asynchronously, and is a completely separate layer.
Your current code is creating two overlapping Surfaces with the same Z-ordering, which means they're trying to occupy the same space at the same time. The results are undefined, but generally speaking you will see one Surface but not the other. Use e.g. setZOrderMediaOverlay() on the one that should be in front of the other. See Grafika's "multi-surface test" Activity for an example with three overlapping Surfaces.
The Surfaces are created asynchronously, and not at the same time, so you should expect your surfaceCreated() callback to fire twice. It looks like you're calling init() each time. If it first fires for Surface A, and init() tries to draw on Surface B, the method will fail, because it's trying to draw on a Surface that hasn't been created yet. Check the value of SurfaceHolder in surfaceCreated(), and and only draw on the Surface that was just created. (Alternatively, wait until they've both been created, and then draw on both.)
I have a SurfaceView that that the user can draw multiple bitmaps to and modify (stickers). The stickers are held in a LinkedList that is iterated on MotionEvent.ACTION_DOWN to find which sticker is being touched by the user:
private void setActiveSticker(float x, float y) {
Iterator<Sticker> stickersDesc = mStickers.descendingIterator();
while (stickersDesc.hasNext()) {
Sticker sticker = stickersDesc.next();
if (sticker.collider(x, y)) {
mActiveSticker = sticker;
mMode = MODE_DRAG;
break;
}
mStickers.remove(mActiveSticker);
mStickers.add(mActiveSticker);
}
}
This LinkedList is also iterated to draw each one to the Canvas of the SurfaceView as they are manipulated:
#Override
public void draw (Canvas canvas) {
super.draw(canvas);
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
for (Sticker sticker : mStickers) {
sticker.draw(canvas);
}
}
And this is where I get a ConcurrentModificationException:
09-28 08:56:41.769 19832-24370/com.example.ex E/AndroidRuntime﹕ FATAL EXCEPTION: Thread-5279
Process: com.example.ex, PID: 19832
java.util.ConcurrentModificationException
at java.util.LinkedList$LinkIterator.next(LinkedList.java:124)
at com.example.ex.utilities.DrawingSurface.draw(DrawingSurface.java:133)
at com.example.ex.utilities.DrawingThread.onSurfaceUpdate(DrawingThread.java:95)
at com.example.ex.utilities.DrawingThread.run(DrawingThread.java:46)
The draw() method of the SurfaceView is called by a separate Thread:
public class DrawingThread extends Thread {
volatile boolean mRunning = false;
private long mRefreshRate;
private DrawingSurface mSurface;
public DrawingThread (DrawingSurface surface, long time) {
super();
mSurface = surface;
mRefreshRate = time;
}
public void setRunning (boolean run) {
mRunning = run;
}
#Override
public void run() {
while (mRunning) {
try {
sleep(mRefreshRate);
onSurfaceUpdate();
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
}
public void onSurfaceChanged(Configuration config, Point fit, float ratio) {
float width, height;
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
width = fit.y * ratio;
height = fit.y;
} else if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
width = fit.x;
height = fit.x / ratio;
} else {
width = fit.x;
height = fit.x / ratio;
} mSurface.getHolder().setFixedSize((int) width, (int) height);
}
private void onSurfaceUpdate() {
Canvas canvas = null;
try {
canvas = mSurface.getHolder().lockCanvas();
synchronized (mSurface.getHolder()) {
mSurface.draw(canvas);
}
} finally {
if (canvas != null) {
mSurface.getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
I have tried pausing the thread before the LinkedList is iterated in setActiveSticker() and resuming after the loop is finished in an attempt to avoid the both modifications from happening at the same time. Even though that does not seem to be recommended. I would like to know how I can iterate the LinkedList without this error, or if there is a better way of achieving the same functionality.
It is normal. You can't change the underlying Collection while you are looping. You can use a ListIterator that has the add and remove items
private void setActiveSticker(float x, float y) {
ListIterator<Sticker> stickersDesc = mStickers.descendingIterator();
while (stickersDesc.hasNext()) {
Sticker sticker = stickersDesc.next();
if (sticker.collider(x, y)) {
stickersDesc.remove();
stickersDesc.add(sticker);
mMode = MODE_DRAG;
return;
}
}
}
I have found a solution. Instead of iterating the LinkedList, an Iterator, or a ListIterator in draw(), which can all produce a ConcurrentModificationException, I converted the LinkedList to a simple array, like so:
Sticker[] stickers = mStickers.toArray(new Sticker[mStickers.size()]);
for(Sticker sticker : stickers) {
canvas.drawBitmap(sticker.getBitmap(), sticker.getMatrix(), sticker.getPaint());
}
I also left setActiveSticker() method as I originally posted it, considering it was not producing any errors. I found the answer I was looking for in a small list of options titled "To Avoid ConcurrentModificationException In [A] Multi-Threaded Environment" in this article: How To Avoid ConcurrentModificationException When Using An Iterator.
EDIT:
My new draw method based on the tip from #fadden:
public void drawSurface(Canvas canvas) {
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
Sticker[] stickers = mStickers.toArray(new Sticker[mStickers.size()]);
for (Sticker sticker : stickers) {
canvas.drawBitmap(sticker.getBitmap(), sticker.getMatrix(), sticker.getPaint());
}
}
I am experimenting with drawing on a canvas using a thread to create a simple game engine but I'm having some weird issues I cannot explain.
The purpose of this "game" is to draw a circle every second on the canvas.
This works, but not the way I want it to work, it seems the app is switching between two canvasses and adding a circle to each canvas so you get a switch between two canvasses every second with the same number of circles but in a different place on the canvas.
I don't know what I'm doing wrong, but I'm not that familiar with Treadding, has it something to do with how many cores my android device has or something like that?
My code is shown below, so I just use a launchthread which uses a layoutfile that links to the animationthread which starts a thread and draws a circle on the canvas every second.
(You can ignore the touchevent, it isn't uses yet).
The project exists out of a main launchthread:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
which uses this layout file:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.androidtesting.AnimationView
android:id="#+id/aview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</FrameLayout>
And my Surfaceview class with an inner Thread class:
class AnimationView extends SurfaceView implements SurfaceHolder.Callback {
private boolean touched = false;
private float touched_x, touched_y = 0;
private Paint paint;
private Canvas c;
private Random random;
private AnimationThread thread;
public AnimationView(Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
thread = new AnimationThread(holder);
}
class AnimationThread extends Thread {
private boolean mRun;
private SurfaceHolder mSurfaceHolder;
public AnimationThread(SurfaceHolder surfaceHolder) {
mSurfaceHolder = surfaceHolder;
paint = new Paint();
paint.setARGB(255,255,255,255);
paint.setTextSize(32);
}
#Override
public void run() {
while (mRun) {
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
doDraw(c);
sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
private void doDraw(Canvas canvas) {
//clear the canvas
//canvas.drawColor(Color.BLACK);
random = new Random();
int w = canvas.getWidth();
int h = canvas.getHeight();
int x = random.nextInt(w-50);
int y = random.nextInt(h-50);
int r = random.nextInt(255);
int g = random.nextInt(255);
int b = random.nextInt(255);
int size = 20;
canvas.drawCircle(x,y,size,paint);
canvas.restore();
}
public void setRunning(boolean b) {
mRun = b;
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public boolean onTouchEvent(MotionEvent event) {
touched_x = event.getX();
touched_y = event.getY();
int action = event.getAction();
switch(action){
case MotionEvent.ACTION_DOWN:
touched = true;
break;
case MotionEvent.ACTION_MOVE:
touched = true;
break;
default:
touched = false;
break;
}
return true;
}
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
}
it seems the app is switching between two canvasses
Yes, this is how it works. It is called double buffering and you need to redraw all the frame each time:
The content of the Surface is never preserved between unlockCanvas() and lockCanvas(), for this reason, every pixel within the Surface area must be written.
So you need this line canvas.drawColor(Color.BLACK) to be uncommented in your code.
And you shouldn't call Thread.sleep(1000) while canvas is locked, it will cause starvation issue.
It sounds like you have this working, but I did just notice a small error that I should point out.
You called canvas.restore() without calling canvas.save() beforehand.
From the Android developer reference for Canvas: "It is an error to call restore() more times than save() was called."
I don't see any reason for you to call canvas.save() in your case, therefore you should remove the call to canvas.restore().
I need to create a live wallpaper where it just pulls an image from the drawable directory.
Is there an example somewhere that I can refer to?
It would be nice if the example also shows how to draw something simple on top of the image as well. If not, its ok =)
the one on the Android.com site just has drawing a cube =(
thanks for any comment
It is very easy))). Use something like this.
In your Engine constructor use something like this
Bitmap _background = BitmapFactory.decodeResource(getResources(), R.drawable.test);
and in your code use this
private final int WEATHER_ANIMATION_INTERVAL = 1000;
private final Handler _handler = new Handler();
private final Runnable weatherAnimation = new Runnable()
{
#Override
public void run()
{
drawNextFrame();
}
};
private void drawNextFrame()
{
final SurfaceHolder holder = getSurfaceHolder();
try {
_canvas = holder.lockCanvas();
if (_canvas != null)
{
drawAnimation(_canvas);
}
}
finally
{
if (_canvas != null)
holder.unlockCanvasAndPost(_canvas);
}
// schedule the next frame
_handler.removeCallbacks(weatherAnimation);
if (_visible)
{
_handler.postDelayed(weatherAnimation, WEATHER_ANIMATION_INTERVAL);
}
return;
}
private void drawAnimation(Canvas c)
{
c.drawBitmap(_background, _xOffset, _yOffset, _paint);
_weather.draw(c, _xOffset, _yOffset, _paint);
}
I hope this help you
I'm trying to work with 3 SurfaceViews on one screen, one on top half (BoardView), one on bottom half (StatusView), and the last one as an extra layer above the top half (TileView) (see main.xml).
I created a class MySurfaceView, which is extended by BoardView, StatusView and TileView.
I've got multiple problems with this.
Let me first give the code.
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/main_background">
<com.niek.test.BoardView
android:id="#+id/boardview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#+id/boardview">
<com.niek.test.StatusView
android:id="#+id/statusview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#F0931E"
android:layout_below="#+id/boardview" />
<com.niek.test.TileView
android:id="#+id/tileview"
android:layout_width="180dip"
android:layout_height="60dip"
android:layout_gravity="bottom"/>
</FrameLayout>
</RelativeLayout>
MainActivity.java:
package com.niek.test;
public class MainActivity extends Activity {
private Board board;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
board = new Board();
BoardView boardView = (BoardView) findViewById(R.id.boardview);
boardView.setBoard(board);
StatusView statusView = (StatusView) findViewById(R.id.statusview);
statusView.setBoard(board);
}
}
MySurfaceView.java
package com.niek.test;
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
protected DrawThread drawThread;
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
setFocusable(true);
drawThread = new DrawThread(getHolder());
}
#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
drawThread.setRunning(true);
drawThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// 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;
drawThread.setRunning(false);
while (retry) {
try {
drawThread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
protected class DrawThread extends Thread {
private SurfaceHolder surfaceHolder;
private boolean isRunning;
public DrawThread(SurfaceHolder surfaceHolder) {
this.surfaceHolder = surfaceHolder;
isRunning = false;
}
public void setRunning(boolean run) {
isRunning = run;
}
public void run() {
Canvas c;
while (isRunning) {
try {
Thread.sleep(100);
} catch (Exception e) {
// TODO: handle exception
}
c = null;
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
onDraw(c);
postInvalidate();
}
} 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);
}
}
}
}
}
}
These three classes extend MySurfaceView:
BoardView.java
package com.niek.test;
public class BoardView extends MySurfaceView {
private int squareSize, marginX, marginY;
private Board board;
Paint boardBorder;
public BoardView(Context context, AttributeSet attrs) {
super(context, attrs);
board = null;
}
public void setBoard(Board board) {
this.board = board;
}
private void init(SurfaceHolder holder) {
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
/* Initialize the board */
squareSize = canvas.getWidth() / Board.GRIDSIZE;
/* Size the view */
LayoutParams lp = getLayoutParams();
lp.height = (squareSize * Board.GRIDSIZE) + 4;
setLayoutParams(lp);
/* Place the board neatly in the center */
marginX = (canvas.getWidth() - (squareSize * Board.GRIDSIZE)) / 2;
marginY = 1;
} finally {
holder.unlockCanvasAndPost(canvas);
}
boardBorder = new Paint();
boardBorder.setColor(Color.RED);
boardBorder.setStyle(Style.STROKE);
}
#Override
public void onDraw(Canvas canvas) {
drawBoard(board, canvas);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
init(holder);
super.surfaceCreated(holder);
}
private void drawBoard(Board board, Canvas canvas) {
synchronized (board) {
if (board != null) {
for (Square[] ys : board.getSquares()) {
for (Square xs : ys) {
xs.onDraw(canvas, squareSize, squareSize, marginX, marginY);
}
}
}
canvas.drawRect(marginX - 1, marginY - 1, marginX + squareSize * Board.GRIDSIZE + 1, marginY + squareSize * Board.GRIDSIZE + 1, boardBorder);
}
}
}
StatusView.java
package com.niek.test;
public class StatusView extends MySurfaceView {
private Board board;
private Paint textPaint;
public StatusView(Context context, AttributeSet attrs) {
super(context, attrs);
board = null;
textPaint = new Paint();
textPaint.setColor(Color.BLACK);
textPaint.setTextSize(20);
textPaint.setTypeface(Typeface.DEFAULT_BOLD);
}
public void setBoard(Board board) {
this.board = board;
}
int tmp=0;
#Override
public void onDraw(Canvas c) {
if (board != null) {
c.drawText(tmp+"", 10, 20, textPaint);
tmp++;
System.out.println(tmp);
}
}
}
TileView.java
package com.niek.test;
public class TileView extends MySurfaceView {
public TileView(Context context, AttributeSet attrs) {
super(context, attrs);
System.out.println(0);
}
int tmp =0;
#Override
public void onDraw(Canvas c) {
System.out.println(2);
Paint p= new Paint();
p.setColor(Color.RED);
c.drawColor(Color.RED);
c.drawText(tmp+"",10,10,p);
tmp++;
}
}
Now what are my problems?
First off, as you can see in MySurfaceView I've got this:
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
onDraw(c);
postInvalidate();
}
}
When I only use onDraw(c), only the BoardView gets drawn, the StatusView doesn't get drawn, but the tmp increments in the onDraw of StatusView are being executed.
When I only use postInvalidate(), same story, but only StatusView gets drawn, BoardView doesn't.
So that's why I use both methods, and both Views get drawn.
Then there's TileView, the System.out(2) is being shown in logcat, but the view doesn't get drawn. It is a black square instead of the red square I ask it to be in the onDraw method.
When I turn the screen off and then on again, the TileView does get drawn, and the tmp increments are shown.
Who can help me?
For clarity, I've created this based on this tutorial.
You can have multiple SurfaceViewsin one layout. The "Multi-surface test" activity in Grafika has three.
The first post cited in #nonsleepr's answer was followed up 9 months later with this post by the same author, which mentioned the existence of SurfaceView#setZOrderMediaOverlay().
The key thing to understand is that SurfaceView is not a regular view. When your app comes to the foreground it gets a surface to draw on. Everything in your app's UI is rendered onto the app's surface by the app, and then that surface is composited with other surfaces (like the status bar and navigation bar) by the system compositor. When you create a SurfaceView, it's actually creating an entirely new surface that is composited by the system, not by your app.
You can control the Z-ordering (i.e. "depth") of the SurfaceView surface very loosely. There are four positions, from top to bottom:
SurfaceView + ZOrderOnTop
(app UI goes here)
SurfaceView + ZOrderMediaOverlay
SurfaceView (default)
If you have two SurfaceViews at the same depth, and they overlap, the results are undefined -- one will "win", but you can't control which.
The system compositor on modern devices is very efficient when you have N surfaces. At N+1 surfaces you hit a performance cliff. So while you can have three SurfaceViews, you're generally better off keeping the number down. The value of N varies from device to device.
Update: if you really want to understand how SurfaceView works, see the Android System-Level Graphics doc.
It looks like you are not supposed to create multiple SurfaceViews on one Layout.
According to this two posts written by Android framework engineer:
The way surface view is implemented is that a separate surface is created and Z-ordered behind its containing window, and transparent pixels drawn into the rectangle where the SurfaceView is so you can see the surface behind. We never intended to allow for multiple surface view.
and
you should effectively think of SurfaceView as an overlay you embed inside your window,
giving you an area in which you can directly draw independently of the normal view update system.
So, what you can do, is use one SurfaceView to draw all the graphics you want.
It sounds like the SurfaceViews are being drawn, but transparency is not enabled for whichever one is on top. In your MySurfaceView class in the surfaceCreated() method, make sure you are calling holder.setFormat(PixelFormat.TRANSPARENT);