Android Multi SurfaceViews
Я пытаюсь работать с 3 SurfaceViews на одном экране, один сверху (BoardView), один на нижней половине (StatusView), а последний - как дополнительный слой над верхней половиной (TileView) (см..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, System.out(2) отображается в logcat, но представление не получается. Это черный квадрат вместо красного квадрата, который я прошу, чтобы он был в методе onDraw.
Когда я выключу и снова включу экран, TileView рисуется, и показаны показания tmp.
Кто может мне помочь?
Для ясности я создал это на основе этого учебника.
Ответы
Ответ 1
Похоже, вы не должны создавать несколько SurfaceViews на одном макете.
В соответствии с этим двумя сообщениями, написанными инженером Android:
Поверхностный вид пути реализован в том, что создается отдельная поверхность и Z-упорядочено позади его содержащего окна, а прозрачные пиксели, втянутые в прямоугольник, где SurfaceView - так, что вы можете видеть поверхность позади. Мы никогда не планировали допускать многократное представление поверхности.
и
вы должны эффективно думать о SurfaceView как оверлей, который вы вставляете в свое окно, предоставляя вам область, в которой вы можете напрямую рисовать независимо от системы обновления обычного вида.
Итак, что вы можете сделать, используется один SurfaceView для рисования всей необходимой графики.
Ответ 2
В одном макете может быть несколько SurfaceViews
. Активность "Multi-surface test" в Grafika имеет три.
Первое сообщение, указанное в ответе @nonsleepr, прошло 9 месяцев спустя с этим сообщением того же автора, в котором говорилось о существовании SurfaceView # setZOrderMediaOverlay().
Главное, чтобы понять, что SurfaceView
не является регулярным представлением. Когда ваше приложение выходит на передний план, он получает поверхность, на которой можно рисовать. Все в пользовательском интерфейсе приложения отображается на поверхность приложения приложением, а затем эта поверхность компонуется с другими поверхностями (например, панель состояния и панель навигации) с помощью системного компоновщика. Когда вы создаете SurfaceView
, он фактически создает совершенно новую поверхность, которая будет скомпонована системой, а не вашим приложением.
Вы можете очень легко управлять Z-упорядочением (т.е. глубиной) поверхности SurfaceView
. Есть четыре позиции сверху вниз:
-
SurfaceView
+ ZOrderOnTop
- (пользовательский интерфейс приложения)
-
SurfaceView
+ ZOrderMediaOverlay
-
SurfaceView
(по умолчанию)
Если у вас есть два SurfaceViews на одной и той же глубине, и они перекрываются, результаты undefined - один будет "выигрывать", но вы не можете управлять им.
Системный композитор на современных устройствах очень эффективен, когда у вас есть N поверхностей. На поверхностях N + 1 вы попадаете в скалу производительности. Поэтому, когда у вас может быть три SurfaceViews
, вам, как правило, лучше держать номер вниз. Значение N варьируется от устройства к устройству.
Обновление:, если вы действительно хотите понять, как работает SurfaceView, см. Android-уровень графического документа Android.
Ответ 3
Похоже, что SurfaceViews нарисованы, но прозрачность не включена для того, что находится сверху. В классе MySurfaceView в методе surfaceCreated() убедитесь, что вы вызываете holder.setFormat(PixelFormat.TRANSPARENT);