16

1つの画面で3つのSurfaceViewを操作しようとしています.1つは上半分(BoardView)、もう1つは下半分(StatusView)、最後の1つは上半分の上の追加レイヤー(TileView)です(main.xmlを参照)。 .

BoardView、StatusView、および TileView によって拡張されるクラス MySurfaceView を作成しました。

これには複数の問題があります。

まずコードを教えてください。

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

}

これら 3 つのクラスは 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 は描画されませんが、StatusView の onDraw で tmp のインクリメントが実行されています。postInvalidate() のみを使用すると、同じ話ですが、StatusView のみが描画され、BoardView は描画されません。そのため、両方の方法を使用し、両方のビューが描画されます。

次に、TileView があり、System.out(2) が logcat に表示されていますが、ビューは描画されません。赤い四角ではなく、黒い四角です。onDraw メソッドで指定します。

画面をオフにしてから再度オンにすると、TileView が描画され、tmp の増分が表示されます。

誰が私を助けることができます?

わかりやすくするために、このチュートリアルに基づいてこれを作成しました。

4

3 に答える 3

70

複数のレイアウトを 1 つのレイアウトに含めることができSurfaceViewsます。Grafikaの「Multi-surface test」アクティビティには 3 つあります。

@nonsleepr の回答で引用された最初の投稿は、 SurfaceView#setZOrderMediaOverlay()の存在について言及した同じ著者によるこの投稿で 9 か月後にフォローアップされました。

理解しておくべき重要なことは、それSurfaceViewは通常のビューではないということです。アプリがフォアグラウンドになると、描画するサーフェスが取得されます。アプリの UI のすべてがアプリによってアプリのサーフェスにレンダリングされ、システム コンポジターによってそのサーフェスが他のサーフェス (ステータス バーやナビゲーション バーなど) と合成されます。を作成するSurfaceViewと、実際には、アプリではなくシステムによって合成されるまったく新しいサーフェスが作成されます。

サーフェスの Z オーダー (つまり「深さ」) をSurfaceView非常に大まかに制御できます。上から下に 4 つの位置があります。

  • SurfaceView+ ZOrderOnTop
  • (アプリの UI がここに表示されます)
  • SurfaceView+ ZOrderMediaOverlay
  • SurfaceView(デフォルト)

同じ深度に 2 つの SurfaceView があり、それらが重なっている場合、結果は未定義です。一方が「勝ち」ますが、どちらを制御することはできません。

最新のデバイスのシステム コンポジターは、N 個のサーフェスがある場合に非常に効率的です。N+1 サーフェスでは、パフォーマンスの崖にぶつかります。したがって、 を3 つ持つこともできSurfaceViewsますが、一般的には数を抑えたほうがよいでしょう。N の値はデバイスによって異なります。

更新: SurfaceView の仕組みを本当に理解したい場合は、Android System-Level Graphics docを参照してください。

于 2014-03-30T16:03:02.683 に答える
11

1 つのレイアウトに複数の SurfaceView を作成することは想定されていないようです。Androidフレームワークエンジニアが書いたこの2つの 投稿によると:

サーフェス ビューが実装される方法は、別のサーフェスが作成され、それを含むウィンドウの背後で Z オーダーされ、透明なピクセルが SurfaceView がある四角形に描画されるため、背後のサーフェスを見ることができます。複数の面を表示できるようにするつもりはありませんでした。

SurfaceView は、ウィンドウ内に埋め込むオーバーレイとして効果的に考える必要があります。これにより、通常のビュー更新システムから独立して直接描画できる領域が提供されます。

したがって、できることは、1 つの SurfaceView を使用して、必要なすべてのグラフィックを描画することです。

于 2011-10-05T12:58:58.287 に答える
1

SurfaceViews が描画されているように聞こえますが、一番上にあるものに対して透明度が有効になっていません。surfaceCreated() メソッドの MySurfaceView クラスで、呼び出していることを確認してくださいholder.setFormat(PixelFormat.TRANSPARENT);

于 2016-02-02T15:56:48.403 に答える