Android多个SurfaceViews

nha*_*man 12 android surfaceview ondraw

我试图在一个屏幕上使用3个SurfaceViews,一个在上半部分(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>
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)

这三个类扩展了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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

    }

}
Run Code Online (Sandbox Code Playgroud)

现在我的问题是什么?

首先,你可以在MySurfaceView中看到我得到了这个:

try {
    c = surfaceHolder.lockCanvas(null);
    synchronized (surfaceHolder) {
        onDraw(c);
        postInvalidate();
    }
}
Run Code Online (Sandbox Code Playgroud)

当我只使用onDraw(c)时,只绘制BoardView,不会绘制StatusView,但是正在执行StatusView的onDraw中的tmp增量.当我只使用postInvalidate(),相同的故事,但只有StatusView被绘制,BoardView没有.这就是为什么我使用这两种方法,并且两个视图都被绘制出来.

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

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

谁能帮我?

为清楚起见,我是根据教程创建的.

fad*_*den 54

可以SurfaceViews在一个布局中使用多个.格拉菲卡的"多表面测试"活动有三个.

在@nonsleepr的回答中引用的第一篇文章在9个月之后被同一作者的帖子跟进,后者提到了SurfaceView#setZOrderMediaOverlay()的存在.

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

您可以SurfaceView非常松散地控制表面的Z排序(即"深度").从上到下有四个位置:

  • SurfaceView + ZOrderOnTop
  • (app UI在这里)
  • SurfaceView + ZOrderMediaOverlay
  • SurfaceView (默认)

如果你有两个相同深度的SurfaceView,并且它们重叠,结果是未定义的 - 一个将"赢",但你无法控制哪个.

当你有N个表面时,现代设备上的系统合成器非常有效.在N + 1个表面,你会遇到性能悬崖.所以虽然你可以有三个SurfaceViews,但你最好还是保持这个数字.N的值因设备而异.

更新:如果您真的想了解SurfaceView的工作原理,请参阅Android系统级图形文档.

  • 这应该标记为答案.谢谢@fadden. (2认同)
  • 对于到达这里但正在使用“GLSurfaceView”的读者:但是,如果您尝试拥有两个“GLSurfaceViews”,而不仅仅是两个“SurfaceViews”,那将无法正常工作。(关于锁定 GL 线程。)因此,Grafika 使用的方法是,他们创建自己的 GL 线程,并管理对该线程上不同“SurfaceViews”的访问。 (2认同)

non*_*epr 11

看起来您不应该在一个Layout上创建多个SurfaceView.根据Android框架工程师编写的这两篇 帖子:

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

您应该有效地将SurfaceView视为嵌入窗口内的叠加层,为您提供一个区域,您可以在该区域中独立于普通视图更新系统进行绘制.

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

  • 这个答案有误导性。从fadden的回答中,通过该工程师发布的链接,我们读到:“在2.0之前,不支持相互叠加的多个表面......从2.0开始,你可以使用一个hack来至少控制Z-order两个表面视图”。关键短语是**在彼此之上**。**非重叠**表面**没有**问题,即使在 2.0 之前也是如此。(有关 Grafika 源代码的链接,请参阅 fadden 的回答,它演示了这一点。)在 2.0 之后,甚至可以处理重叠的表面,只需稍加努力。 (2认同)