Android Несколько SurfaceViews

Я пытаюсь работать с 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>

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

}

эти три класса расширяют 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++;

    }

}

теперь, что мои проблемы?

во-первых, как вы можете видеть в MySurfaceView, у меня есть это:

try {
    c = surfaceHolder.lockCanvas(null);
    synchronized (surfaceHolder) {
        onDraw(c);
        postInvalidate();
    }
}

когда я использую только onDraw (c), только BoardView рисуется, StatusView не рисуется, но инкременты tmp в onDraw StatusView выполняются. Когда я использую только postInvalidate (), та же история, но только StatusView рисуется, BoardView нет. Вот почему я использую оба метода, и оба представления рисуются.

тогда есть TileView, система.out (2) отображается в logcat, но представление не рисуется. Это Черный квадрат вместо красного квадрата, я прошу его быть в onDraw метод.

когда я выключаю экран, а затем снова включаю, TileView действительно рисуется, и показываются приращения tmp.

кто может мне помочь?

для ясности, я создал это на основе этой учебник.

3 ответов


похоже, что вы не должны создавать несколько SurfaceViews на одном макете. Согласно этому два посты написано инженером Android framework:

способ представления поверхности заключается в том, что создается отдельная поверхность и Z-упорядочивается за содержащим ее окном, а прозрачные пиксели рисуются в прямоугольник, где SurfaceView, чтобы вы могли видеть поверхность позади. Мы никогда не собирались допускать множественности вид поверхности.

и

вы должны эффективно думать о SurfaceView как наложение, которое вы вставляете в свое окно, предоставление области, в которой вы можете напрямую рисовать независимо от обычной системы обновления вида.

Итак, что вы можете сделать, это использовать один SurfaceView, чтобы нарисовать всю графику, которую вы хотите.


вы can несколько SurfaceViewsв одном макете. Активность "Multi-surface test" в графика три.

первый пост, указанный в ответе @nonsleepr, был дополнен 9 месяцами спустя этот пост тем же автором, который упомянул о существовании SurfaceView#setZOrderMediaOverlay ().

главное, чтобы понять, что SurfaceView не является обычным представлением. Когда ваше приложение приходит в на переднем плане он получает поверхность для рисования. Все в пользовательском интерфейсе вашего приложения отображается на поверхность приложения приложением, а затем эта поверхность составляется с другими поверхностями (например, строка состояния и панель навигации) системным составителем. При создании SurfaceView, он фактически создает совершенно новую поверхность, которая составляется системой, а не вашим приложением.

вы можете управлять Z-порядком (т. е. "глубиной")SurfaceView поверхность очень слабо. Есть четыре позиции, сверху вниз:

  • SurfaceView + ZOrderOnTop
  • (App UI идет здесь)
  • SurfaceView + ZOrderMediaOverlay
  • SurfaceView (по умолчанию)

если у вас есть два SurfaceViews на той же глубине, и они перекрываются, результаты не определены-один "выиграет", но вы не можете контролировать, какой.

композитор системы на современных устройствах очень эффективен, когда у вас есть N поверхностей. На N + 1 поверхностях вы ударяете a производительность Клифф. Так что пока ты can три SurfaceViews, вам обычно лучше держать номер вниз. Значение N варьируется от устройства к устройству.

обновление: если вы действительно хотите понять, как работает SurfaceView, см. Android системы уровня графики doc.


похоже, что SurfaceViews рисуются, но прозрачность не включена для того, кто находится сверху. В классе MySurfaceView в методе surfaceCreated () убедитесь, что вы вызываете holder.setFormat(PixelFormat.TRANSPARENT);