Android Multiple SurfaceViews

Estoy tratando de trabajar con 3 SurfaceViews en una pantalla, una en la mitad superior (BoardView), una en la mitad inferior (StatusView), y la última como una capa extra por encima de la mitad superior (TileView) (ver main.xml) .

He creado una clase MySurfaceView, que se amplía por BoardView, StatusView y TileView.

Tengo varios problemas con esto.

Permítanme primero dar el código.

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

Estas tres clases extienden 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++; } } 

Ahora, ¿cuáles son mis problemas?

En primer lugar, como se puede ver en MySurfaceView Tengo esto:

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

Cuando sólo utilizo onDraw (c), sólo se dibuja el BoardView, el StatusView no se dibuja, pero los incrementos tmp en el onDraw de StatusView se están ejecutando. Cuando sólo uso postInvalidate (), la misma historia, pero sólo StatusView se dibuja, BoardView no. Así que es por eso que uso ambos métodos, y ambas vistas se dibujan.

Luego hay TileView, el System.out (2) se muestra en logcat, pero la vista no se dibuja. Es un cuadrado negro en lugar del cuadrado rojo que le pido que esté en el método onDraw.

Cuando apague la pantalla y vuelva a encenderla, el TileView se dibuja y se muestran los incrementos de tmp.

¿Quién puede ayudarme?

Para mayor claridad, he creado esto basado en este tutorial.

Parece que no se supone que se crean múltiples SurfaceViews en un Layout. De acuerdo con estos dos puestos escritos por el ingeniero de framework de Android:

La forma en que se implementa la vista de superficie es que se crea una superficie separada y Z-ordenada detrás de su ventana contenedora, y los píxeles transparentes dibujados en el rectángulo donde está SurfaceView para que pueda ver la superficie detrás. Nunca pensamos permitir una vista superficial múltiple.

y

Debe pensar efectivamente en SurfaceView como una superposición que inserta dentro de su ventana, dándole un área en la que puede dibujar directamente independientemente del sistema de actualización de vista normal.

Por lo tanto, lo que puede hacer, es utilizar un SurfaceView para dibujar todos los gráficos que desee.

Puede tener varias SurfaceViews en un diseño. La actividad de "prueba de superficie múltiple" en Grafika tiene tres.

El primer post mencionado en la respuesta de @ nonsleepr fue seguido 9 meses después con este post por el mismo autor, que mencionó la existencia de SurfaceView # setZOrderMediaOverlay () .

La clave para entender es que SurfaceView no es una vista regular. Cuando su aplicación llega al primer plano, obtiene una superficie para dibujar. Todo en la interfaz de usuario de la aplicación se traduce en la superficie de la aplicación por la aplicación y, a continuación, esa superficie se compone con otras superficies (como la barra de estado y la barra de navegación) por el compositor del sistema. Cuando crea un SurfaceView , en realidad está creando una superficie totalmente nueva que está compuesta por el sistema, no por su aplicación.

Puede controlar el orden Z (es decir, "profundidad") de la superficie de SurfaceView muy libremente. Hay cuatro posiciones, de arriba a abajo:

  • SurfaceView + ZOrderOnTop
  • (La aplicación UI va aquí)
  • SurfaceView + ZOrderMediaOverlay
  • SurfaceView (predeterminado)

Si tiene dos SurfaceViews a la misma profundidad, y se superponen, los resultados son indefinidos – se "ganará", pero no se puede controlar qué.

El sistema compositor en dispositivos modernos es muy eficiente cuando tienes N superficies. En las superficies N + 1 se golpea un acantilado de rendimiento. Así que mientras usted puede tener tres SurfaceViews , en SurfaceViews , es mejor mantener el número hacia abajo. El valor de N varía de un dispositivo a otro.

Actualización: si realmente quieres entender cómo funciona SurfaceView, consulta el documento de gráficos de nivel de sistema de Android .

Parece que las SurfaceViews están siendo dibujadas, pero la transparencia no está habilitada para cualquiera que esté arriba. En su clase MySurfaceView en el método surfaceCreated (), asegúrese de que está llamando a holder.setFormat(PixelFormat.TRANSPARENT);

  • Convertir vista a mapa de bits en Android
  • Margen de visión establecido para Android por programación
  • Uso de Javascript en la vista web
  • Android searchVer no en actionBar eliminar foco
  • SearchView edittext siempre es nulo
  • Debe pasar en una vista no nula
  • ¿Cómo puedo hacer que `View` llene un` RelativeLayout`?
  • ¿Cómo puedo ver la información de mi base de datos de aplicaciones de Android?
  • En Android: ¿Cómo definir la animación adecuada para maximizar una vista mientras se minimizan otros 2 al mismo tiempo?
  • ¿Cómo puedo inhabilitar una vista detrás de mi SlidingDrawer en Android?
  • Eliminar un OnClickListener en una vista quitar todos los eventos de toque en las vistas detrás
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.