简体   繁体   English

Android多个SurfaceViews

[英]Android Multiple SurfaceViews

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). 我试图在一个屏幕上使用3个SurfaceViews,一个在上半部分(BoardView),一个在下半部分(StatusView),最后一个作为上半部分之上的额外层(TileView)(参见main.xml) 。

I created a class MySurfaceView, which is extended by BoardView, StatusView and TileView. 我创建了一个MySurfaceView类,它由BoardView,StatusView和TileView扩展。

I've got multiple problems with this. 我有很多问题。

Let me first give the code. 我先给出代码。

main.xml: 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: 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 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: 这三个类扩展了MySurfaceView:

BoardView.java 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 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 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: 首先,你可以在MySurfaceView中看到我得到了这个:

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. 当我只使用onDraw(c)时,只绘制BoardView,不会绘制StatusView,但是正在执行StatusView的onDraw中的tmp增量。 When I only use postInvalidate(), same story, but only StatusView gets drawn, BoardView doesn't. 当我只使用postInvalidate(),相同的故事,但只有StatusView被绘制,BoardView没有。 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. 然后是TileView,System.out(2)显示在logcat中,但视图没有被绘制。 It is a black square instead of the red square I ask it to be in the onDraw method. 这是一个黑色方块,而不是我要求它在onDraw方法中的红色方块。

When I turn the screen off and then on again, the TileView does get drawn, and the tmp increments are shown. 当我关闭屏幕然后再打开时,TileView会被绘制,并显示tmp增量。

Who can help me? 谁能帮我?

For clarity, I've created this based on this tutorial. 为清楚起见,我是根据教程创建的。

You can have multiple SurfaceViews in one layout. 可以在一个布局中拥有多个SurfaceViews 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() . 在@nonsleepr的回答中引用的第一篇文章在9个月之后被同一作者的帖子跟进,后者提到了SurfaceView#setZOrderMediaOverlay()的存在。

The key thing to understand is that SurfaceView is not a regular view. 要理解的关键是SurfaceView不是常规视图。 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. 应用程序UI中的所有内容都由应用程序呈现在应用程序的表面上,然后系统合成器将该表面与其他表面(如状态栏和导航栏)合成。 When you create a SurfaceView , it's actually creating an entirely new surface that is composited by the system, not by your app. 当您创建SurfaceView ,它实际上创建了一个由系统合成的全新曲面,而不是您的应用程序。

You can control the Z-ordering (ie "depth") of the SurfaceView surface very loosely. 您可以非常松散地控制SurfaceView表面的Z排序(即“深度”)。 There are four positions, from top to bottom: 从上到下有四个位置:

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

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. 如果你有两个相同深度的SurfaceView,并且它们重叠,结果是未定义的 - 一个将“赢”,但你无法控制哪个。

The system compositor on modern devices is very efficient when you have N surfaces. 当你有N个表面时,现代设备上的系统合成器非常有效。 At N+1 surfaces you hit a performance cliff. 在N + 1个表面,你会遇到性能悬崖。 So while you can have three SurfaceViews , you're generally better off keeping the number down. 因此,虽然您可以拥有三个SurfaceViews ,但通常最好还是保持数字下降。 The value of N varies from device to device. N的值因设备而异。

Update: if you really want to understand how SurfaceView works, see the Android System-Level Graphics doc . 更新:如果您真的想了解SurfaceView的工作原理,请参阅Android系统级图形文档

It looks like you are not supposed to create multiple SurfaceViews on one Layout. 看起来您不应该在一个Layout上创建多个SurfaceView。 According to this two posts written by Android framework engineer: 根据Android框架工程师编写的这两篇 帖子

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. 实现曲面视图的方法是创建一个单独的曲面,并在其包含窗口后面进行Z排序,并将透明像素绘制到SurfaceView所在的矩形中,以便您可以看到背后的曲面。 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. 您应该有效地将SurfaceView视为嵌入窗口内的叠加层,为您提供一个可以直接绘制的区域,可以独立于普通视图更新系统。

So, what you can do, is use one SurfaceView to draw all the graphics you want. 因此,您可以使用一个SurfaceView绘制所需的所有图形。

It sounds like the SurfaceViews are being drawn, but transparency is not enabled for whichever one is on top. 这听起来像正在绘制SurfaceViews,但是对于顶部的任何一个都没有启用透明度。 In your MySurfaceView class in the surfaceCreated() method, make sure you are calling holder.setFormat(PixelFormat.TRANSPARENT); 在surfaceCreated()方法的MySurfaceView类中,确保调用holder.setFormat(PixelFormat.TRANSPARENT);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM