Android多个SurfaceView

Android多个SurfaceView,android,surfaceview,ondraw,Android,Surfaceview,Ondraw,我尝试在一个屏幕上使用3个SurfaceView,一个在上半部分(BoardView),一个在下半部分(StatusView),最后一个作为上半部分(TileView)上方的额外层(请参见main.xml) 我创建了一个类MySurfaceView,它由BoardView、StatusView和TileView扩展 我有很多问题 让我先给出代码 main.xml: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout

我尝试在一个屏幕上使用3个SurfaceView,一个在上半部分(BoardView),一个在下半部分(StatusView),最后一个作为上半部分(TileView)上方的额外层(请参见main.xml)

我创建了一个类MySurfaceView,它由BoardView、StatusView和TileView扩展

我有很多问题

让我先给出代码

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

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

    }

}
这三个类扩展了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 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);
                    }
                }
            }
        }
    }

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

    }

}
现在我的问题是什么

首先,正如您在MySurfaceView中看到的,我有以下内容:

try {
    c = surfaceHolder.lockCanvas(null);
    synchronized (surfaceHolder) {
        onDraw(c);
        postInvalidate();
    }
}
当我只使用onDraw(c)时,只绘制BoardView,不绘制StatusView,但正在执行StatusView onDraw中的tmp增量。 当我只使用postInvalidate()时,会绘制相同的故事,但只绘制StatusView,而BoardView不会。 这就是为什么我使用这两种方法,两种视图都被绘制出来

然后是TileView,System.out(2)显示在logcat中,但视图没有绘制出来。它是一个黑色的正方形,而不是红色的正方形,我要求它是在onDraw方法中

当我关闭屏幕然后再次打开时,会绘制TileView,并显示tmp增量

谁能帮我


为清楚起见,我根据教程创建了此视图。

看起来您不应该在一个布局上创建多个SurfaceView。 根据Android框架工程师撰写的这篇文章:

曲面视图的实现方式是在其包含窗口后面创建一个单独的曲面并按Z顺序排列,并将透明像素绘制到曲面视图所在的矩形中,以便可以看到后面的曲面。我们从未打算允许多个曲面视图

您应该有效地将SurfaceView视为嵌入窗口中的覆盖层, 为您提供一个区域,在该区域中,您可以独立于法线视图更新系统直接绘制

因此,您可以使用一个SurfaceView来绘制所需的所有图形。

在一个布局中可以有多个
SurfaceView
。中的“多表面测试”活动有三个方面

@nonsleepr的回答中引用的第一篇帖子在9个月后被同一作者跟进,该作者提到了

要理解的关键是
SurfaceView
不是常规视图。当你的应用程序出现在前台时,它会得到一个可以绘制的表面。应用程序UI中的所有内容都由应用程序渲染到应用程序的曲面上,然后由系统合成器将该曲面与其他曲面(如状态栏和导航栏)合成。当您创建一个
SurfaceView
时,它实际上是在创建一个由系统而不是应用程序合成的全新曲面

您可以非常松散地控制
SurfaceView
曲面的Z顺序(即“深度”)。从上到下有四个位置:

  • SurfaceView
    +ZOrderOnTop
  • (应用程序界面在这里)
  • SurfaceView
    +ZOrderMediaOverlay
  • SurfaceView
    (默认)
如果在同一深度有两个SurfaceView,并且它们重叠,结果是未定义的——一个将“获胜”,但您无法控制哪个

当您有N个曲面时,现代设备上的系统合成器非常有效。在N+1曲面上,您会遇到性能悬崖。因此,虽然您可以拥有三个
SurfaceViews
,但一般来说,您最好将数字保持在较低的水平。N的值因设备而异


更新:如果您真的想了解SurfaceView是如何工作的,请参阅。

听起来好像正在绘制SurfaceView,但顶部的任何一个都不启用透明度。在surfaceCreated()方法的MySurfaceView类中,确保调用的是
holder.setFormat(PixelFormat.TRANSPARENT)

那么我可以在不同的家长中添加两个surfaceview吗?这个答案有误导性。从fadden的回答中,在该工程师的帖子链接后,我们读到:“在2.0之前,不支持多个相互重叠的曲面……从2.0开始,有一个黑客可以用来至少控制两个曲面视图的Z顺序”。关键短语相互重叠。即使在2.0之前,非重叠曲面也没有问题。(参见fadden的答案,链接到Grafika source,它演示了这一点。)2.0之后,即使是重叠的表面也可以处理,只需一点努力。这应该被标记为答案。谢谢你@fadden@fadden:嗨,首先,谢谢你的回答!如果有两个SurfaceView,而其中一个总是隐藏的(View.INVISIBLE)?对于那些来到这里但正在使用
GLSurfaceView
的读者来说:但是,如果您试图有两个
GLSurfaceView
,而不仅仅是两个
surfaceView
,那就行不通了。(关于锁定GL线程的内容)因此Grafika使用的方法是创建自己的GL线程,并管理对该线程上不同
SurfaceView
的访问。这应该被标记为答案。多谢各位@fadden@ToolmakerSteve嘿,你能告诉我你在格拉菲卡密码里到底指的是什么吗?我需要同时使用GLSURFACHEVIEW和SURFACHEVIEW,并面临类似的问题。