java - 在 Android 上,onCompletion 播放声音后不会重绘画布
问题描述
我正在为课堂作业制作迷宫游戏,其中一个要求是在游戏开始时和玩家到达出口时播放声音。
我使用 onCompletionListener 是因为当玩家到达出口时,我必须等到声音播放完毕才能在下一个级别开始播放声音,否则应用程序将挂起。
但是,现在调用 createMaze() 方法后屏幕不会被重绘。如果玩家再次移动,则只有重新绘制画布并显示新创建的迷宫。
这是我期望代码执行的操作:
- 玩家到达出口
- 播放退出声音
- 播放完声音后,新建一个迷宫并在屏幕上绘制
这是我的代码:
package com.example.labirinto;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.media.MediaPlayer;
import android.os.Vibrator;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Random;
import java.util.Stack;
public class GameView extends View implements MediaPlayer.OnCompletionListener {
private enum Direction {
UP, DOWN, LEFT, RIGHT
}
private Vibrator vibrator;
private SensorManager sensorManager;
private Sensor gyroscopeSensor;
private static final int MAX_LEVELS = 3;
private int currentLevel = 1;
private String nextAction;
private Cell[][] cells;
private Cell player, exit;
private static final int COLS = 7, ROWS = 10;
private static final String
ACTION_CREATE_MAZE = "ACTION_CREATE_MAZE",
ACTION_END_GAME = "ACTION_END_GAME";
private static final float WALL_THICKNESS = 4;
private float cellSize, hMargin, vMargin;
private Paint wallPaint, playerPaint, exitPaint;
private Random random;
MediaPlayer mp;
public GameView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
wallPaint = new Paint();
wallPaint.setColor(Color.BLACK);
wallPaint.setStrokeWidth(WALL_THICKNESS);
playerPaint = new Paint();
playerPaint.setColor(Color.RED);
exitPaint = new Paint();
exitPaint.setColor(Color.BLUE);
random = new Random();
vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
gyroscopeSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
createMaze();
}
private void createMaze() {
Stack<Cell> stack = new Stack<>();
Cell current, next;
cells = new Cell[COLS][ROWS];
for (int x = 0; x < COLS; x++) {
for (int y = 0; y < ROWS; y++) {
cells[x][y] = new Cell(x, y);
}
}
player = cells[0][0];
exit = cells[COLS - 1][ROWS - 1];
current = cells[0][0];
current.visited = true;
do {
next = getNeighbour(current);
if (next != null) {
removeWall(current, next);
stack.push(current);
current = next;
current.visited = true;
} else {
current = stack.pop();
}
} while (!stack.empty());
nextAction = "NOTHING";
playSound("START");
}
private Cell getNeighbour(Cell cell) {
ArrayList<Cell> neighbours = new ArrayList<>();
//left neighbour
if (cell.col > 0 && !cells[cell.col - 1][cell.row].visited) {
neighbours.add(cells[cell.col - 1][cell.row]);
}
//right neighbour
if (cell.col < (COLS - 1) && !cells[cell.col + 1][cell.row].visited) {
neighbours.add(cells[cell.col + 1][cell.row]);
}
//top neighbour
if (cell.row > 0 && !cells[cell.col][cell.row - 1].visited) {
neighbours.add(cells[cell.col][cell.row - 1]);
}
//bottom neighbour
if (cell.row < (ROWS - 1) && !cells[cell.col][cell.row + 1].visited) {
neighbours.add(cells[cell.col][cell.row + 1]);
}
if (neighbours.size() > 0 ) {
int index = random.nextInt(neighbours.size());
return neighbours.get(index);
}
return null;
}
private void removeWall(Cell current, Cell next) {
//current under next
if (current.col == next.col && current.row == next.row + 1) {
current.topWall = false;
next.bottomWall = false;
}
//current above next
if (current.col == next.col && current.row == next.row - 1) {
current.bottomWall = false;
next.topWall = false;
}
//current to the right of the next
if (current.col == next.col + 1 && current.row == next.row) {
current.leftWall = false;
next.rightWall = false;
}
//current to the left of the next
if (current.col == next.col - 1 && current.row == next.row) {
current.rightWall = false;
next.leftWall = false;
}
}
public void drawCurrentLevelText(Canvas canvas) {
Paint textPaint = new Paint();
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setColor(Color.BLACK);
textPaint.setTextSize(48f);
int xPos = (canvas.getWidth() / 2);
canvas.drawText("Level " + currentLevel, xPos, 56, textPaint);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
drawCurrentLevelText(canvas);
int width = getWidth();
int height = getHeight();
if (width/height < COLS/ROWS) {
cellSize = width/(COLS + 1) - 8;
} else {
cellSize = height/(ROWS + 1) - 8;
}
hMargin = (width - COLS*cellSize)/2;
vMargin = (height - ROWS*cellSize)/2;
canvas.translate(hMargin, vMargin);
if (currentLevel == 2) {
wallPaint.setColor(Color.BLUE);
} else if (currentLevel == 3) {
wallPaint.setColor(Color.RED);
}
for (int x = 0; x < COLS; x++) {
for (int y = 0; y < ROWS; y++) {
if (cells[x][y].topWall) {
canvas.drawLine(x*cellSize, y*cellSize, (x + 1)*cellSize, y*cellSize, wallPaint);
}
if (cells[x][y].leftWall && !(x == 0 && y == 0)) {
canvas.drawLine(x*cellSize, y*cellSize, x*cellSize, (y+1)*cellSize, wallPaint);
}
if (cells[x][y].rightWall && !(x == COLS - 1 && y == ROWS - 1)) {
canvas.drawLine((x+1)*cellSize, y*cellSize, (x + 1)*cellSize, (y+1)*cellSize, wallPaint);
}
if (cells[x][y].bottomWall) {
canvas.drawLine(x*cellSize, (y+1)*cellSize, (x+1)*cellSize, (y+1)*cellSize, wallPaint);
}
}
}
float margin = cellSize/10;
canvas.drawRect(
player.col*cellSize+margin,
player.row*cellSize+margin,
(player.col + 1)*cellSize-margin,
(player.row + 1)*cellSize-margin,
playerPaint
);
}
private void movePlayer(Direction direction) {
switch (direction) {
case UP:
if (!player.topWall) {
player = cells[player.col][player.row - 1];
} else {
vibrator.vibrate(400);
}
break;
case DOWN:
if (!player.bottomWall) {
player = cells[player.col][player.row + 1];
} else {
vibrator.vibrate(400);
}
break;
case LEFT:
if (!(player.leftWall || player.col == 0)) {
player = cells[player.col - 1][player.row];
} else {
vibrator.vibrate(400);
}
break;
case RIGHT:
if (!player.rightWall) {
player = cells[player.col + 1][player.row];
} else {
vibrator.vibrate(400);
}
break;
}
checkExit();
invalidate();
}
private void playSound(String type) {
int resId;
switch (type) {
case "START":
resId = R.raw.start;
break;
case "ERROR":
resId = R.raw.retardado;
break;
case "EXIT":
resId = R.raw.miseravel_genio;
break;
default:
resId = 0;
break;
}
mp = MediaPlayer.create(getContext(), resId);
mp.setOnCompletionListener(this);
mp.start();
}
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mp.release();
switch (nextAction) {
case ACTION_CREATE_MAZE:
currentLevel++;
createMaze();
break;
case ACTION_END_GAME:
getContext().startActivity(new Intent(getContext(),com.example.labirinto.GameOver.class));
break;
default:
System.out.println("Finished");
break;
}
}
private void checkExit() {
if (player == exit) {
if (currentLevel < MAX_LEVELS) {
nextAction = ACTION_CREATE_MAZE;
} else {
nextAction = ACTION_END_GAME;
}
playSound("EXIT");
}
}
public void onSensorChanged(SensorEvent event) {
System.out.println(event.sensor.getName());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
return true;
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
float x = event.getX();
float y = event.getY();
float playerCenterX = hMargin + (player.col + 0.5f)*cellSize;
float playerCenterY = vMargin + (player.row + 0.5f)*cellSize;
float dx = x - playerCenterX;
float dy = y - playerCenterY;
float absDx = Math.abs(dx);
float absDy = Math.abs(dy);
if (absDx > cellSize || absDy > cellSize) {
if (absDx > absDy) {
//move in x-direction
if (dx > 0) {
//move to the right
movePlayer(Direction.RIGHT);
} else {
//move to the left
movePlayer(Direction.LEFT);
}
} else {
//move in y-direction
if (dy > 0) {
//move down
movePlayer(Direction.DOWN);
} else {
//move up
movePlayer(Direction.UP);
}
}
}
return true;
}
return super.onTouchEvent(event);
}
private class Cell {
boolean topWall = true, leftWall = true, bottomWall = true, rightWall = true, visited = false;
int col , row;
public Cell(int col, int row) {
this.col = col;
this.row = row;
}
}
}
在 checkExit() 方法中,如果玩家已经到达出口,我会检查它是否是定义下一个动作的最终关卡,然后播放退出声音。
然后,在 playSound() 方法中,我选择要播放的声音,设置监听器并播放声音。
最后,在 onCompletion() 方法上,我释放调用 MediaPlayer 对象的 release() 方法,然后调用下一个操作。
解决方案
因此,我能够通过在 onCompletion() 方法中调用 invalidate() 来找到解决方案。通过这样做,画布会自动重绘。
不过,我不确定这是否是解决此问题的最佳方法。
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mp.release();
switch (nextAction) {
case ACTION_CREATE_MAZE:
currentLevel++;
createMaze();
invalidate();
break;
case ACTION_END_GAME:
getContext().startActivity(new Intent(getContext(),com.example.labirinto.GameOver.class));
invalidate();
break;
default:
System.out.println("Finished");
break;
}
}
推荐阅读
- r - 在虚拟变量(或接近)之前/之后一年创建事件(虚拟)
- windows - 使用标准输出作为日志路径
- reactjs - 在 React 应用程序中使用 typescript 超链接到 pdf 文件
- java - IntelliJ 无法加载 Maven 依赖项
- ios - scrollView 中只添加了第一个 customUIView
- java - 如何处理 DatabaseException 异常?
- php - 删除 index.php 时如何通过 CLI 执行 Codeigniter 端点
- java - 为什么使用封闭类的实例,静态嵌套类不能实例化?
- vba - 如何使用 Office 脚本自动过滤数据透视表?
- node.js - 在 Pop-OS/Ubuntu 更新后找不到模块“../build/Release/zmq.node”