Android SurfaceView:在图像后显示视频
Android SurfaceView: Show Video after Images
我想在同一个 SurfaceView 中的几个图像之后播放视频。如果我在视频后显示图像,一切正常。但是如果我想在图像之后播放视频,SurfaceView 将保持空白(黑色)。谁能帮忙?好像是清除或重置SurfaceView的问题!
SurfaceView的代码(自定义SurfaceView!!!):
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, MediaPlayer.OnCompletionListener {
private Bitmap image;
private MediaPlayer mediaPlayer;
private List<MySurfaceListener> objectsToCall = new ArrayList<>();
public MySurfaceView(Context context) {
super(context);
getHolder().addCallback(this);
}
public void addOnPlayingFinishedListener(MySurfaceListener listener) {
objectsToCall.add(listener);
}
public void notifyAllOnPlayingFinishedListener() {
for(MySurfaceListener l : objectsToCall) {
l.onMediaPlayFinished();
}
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Helper method to scale down the image files
}
public static Bitmap decodeSampledBitmapFromFile(String path, int reqWidth, int reqHeight) {
// Another helper method for the image files
}
public void drawImage(String path, final int durationMs) {
this.image = decodeSampledBitmapFromFile(path, 1920, 1080);
Canvas canvas = null;
try {
canvas = getHolder().lockCanvas(null);
synchronized (getHolder()) {
onDraw(canvas);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (canvas != null) {
getHolder().unlockCanvasAndPost(canvas);
}
image.recycle();
image = null;
}
Thread showPictureTimer = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(durationMs);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
showPictureTimer.start();
try {
showPictureTimer.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
notifyAllOnPlayingFinishedListener();
}
public void playVideo(String path) {
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setDisplay(getHolder());
mediaPlayer.setDataSource(path);
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IllegalArgumentException e) {
Log.d("MyMediaPlayerControl", e.getMessage());
} catch (IllegalStateException e) {
Log.d("MyMediaPlayerControl", e.getMessage());
} catch (IOException e) {
Log.d("MyMediaPlayerControl", e.getMessage());
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(this.image, 0, 0, new Paint());
}
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
Log.e("myApp", "surfaceChanged");
}
public void surfaceCreated(SurfaceHolder arg0) {
Log.e("myApp", "surfaceCreated");
}
public void surfaceDestroyed(SurfaceHolder arg0) {
Log.e("myApp", "surfaceDestroyed");
}
@Override
public void onCompletion(MediaPlayer mp) {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
notifyAllOnPlayingFinishedListener();
}
}
以及将文件路径提供给表面视图进行播放的线程(应该不是问题所在):
public class MediaControlThread extends Thread implements MySurfaceListener {
public static List<String> mediaList = new ArrayList<String>();
MyMediaActivity activity;
private MySurfaceView surfaceView;
private int currentVideo = 0;
public MediaControlThread(MyMediaActivity activity) {
this.activity = activity;
this.surfaceView = activity.getSurfaceView();
this.surfaceView.addOnPlayingFinishedListener(this);
}
@Override
public void run() {
try {
while(mediaList.size() == 0) {
Thread.sleep(1000);
}
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
playMedia(mediaList.get(currentVideo));
}
private void playMedia(String mediaPath) {
if(checkFileForImage(mediaPath)) {
surfaceView.drawImage(mediaPath, 2000);
} else {
surfaceView.playVideo(mediaPath);
}
}
private boolean checkFileForImage(String fileName) {
String file = fileName.toLowerCase();
return file.endsWith(".jpg") ||
file.endsWith(".img") ||
file.endsWith(".bmp") ||
file.endsWith(".jpeg") ||
file.endsWith(".ico") ||
file.endsWith(".tif");
}
@Override
public void onMediaPlayFinished() {
surfaceView.invalidate();
surfaceView.destroyDrawingCache();
currentVideo++;
if (currentVideo > mediaList.size() - 1) {
currentVideo = 0;
}
playMedia(mediaList.get(currentVideo));
}
}
不幸的是,这是预期的行为。由于 Android 中的一个问题,一旦您使用 Canvas 在 Surface 上绘制,您将无法执行任何其他操作。
你有三个选择:
- 使用 OpenGL ES 而非 Canvas 绘制图像。分离 GLES 后,您可以返回显示视频。
- 将图片呈现为单帧视频。这适用于一些琐碎的事情,比如让屏幕空白,但对于动态内容来说是一个糟糕的选择。
- 使用 FrameLayout 覆盖 ImageView 或自定义视图。当你想显示图像时显示视图,否则隐藏它。只要 Surface 处于默认的 Z 深度,ImageView 就会出现在它的前面并遮住它。
我想在同一个 SurfaceView 中的几个图像之后播放视频。如果我在视频后显示图像,一切正常。但是如果我想在图像之后播放视频,SurfaceView 将保持空白(黑色)。谁能帮忙?好像是清除或重置SurfaceView的问题!
SurfaceView的代码(自定义SurfaceView!!!):
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, MediaPlayer.OnCompletionListener {
private Bitmap image;
private MediaPlayer mediaPlayer;
private List<MySurfaceListener> objectsToCall = new ArrayList<>();
public MySurfaceView(Context context) {
super(context);
getHolder().addCallback(this);
}
public void addOnPlayingFinishedListener(MySurfaceListener listener) {
objectsToCall.add(listener);
}
public void notifyAllOnPlayingFinishedListener() {
for(MySurfaceListener l : objectsToCall) {
l.onMediaPlayFinished();
}
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Helper method to scale down the image files
}
public static Bitmap decodeSampledBitmapFromFile(String path, int reqWidth, int reqHeight) {
// Another helper method for the image files
}
public void drawImage(String path, final int durationMs) {
this.image = decodeSampledBitmapFromFile(path, 1920, 1080);
Canvas canvas = null;
try {
canvas = getHolder().lockCanvas(null);
synchronized (getHolder()) {
onDraw(canvas);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (canvas != null) {
getHolder().unlockCanvasAndPost(canvas);
}
image.recycle();
image = null;
}
Thread showPictureTimer = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(durationMs);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
showPictureTimer.start();
try {
showPictureTimer.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
notifyAllOnPlayingFinishedListener();
}
public void playVideo(String path) {
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setDisplay(getHolder());
mediaPlayer.setDataSource(path);
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IllegalArgumentException e) {
Log.d("MyMediaPlayerControl", e.getMessage());
} catch (IllegalStateException e) {
Log.d("MyMediaPlayerControl", e.getMessage());
} catch (IOException e) {
Log.d("MyMediaPlayerControl", e.getMessage());
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(this.image, 0, 0, new Paint());
}
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
Log.e("myApp", "surfaceChanged");
}
public void surfaceCreated(SurfaceHolder arg0) {
Log.e("myApp", "surfaceCreated");
}
public void surfaceDestroyed(SurfaceHolder arg0) {
Log.e("myApp", "surfaceDestroyed");
}
@Override
public void onCompletion(MediaPlayer mp) {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
notifyAllOnPlayingFinishedListener();
}
}
以及将文件路径提供给表面视图进行播放的线程(应该不是问题所在):
public class MediaControlThread extends Thread implements MySurfaceListener {
public static List<String> mediaList = new ArrayList<String>();
MyMediaActivity activity;
private MySurfaceView surfaceView;
private int currentVideo = 0;
public MediaControlThread(MyMediaActivity activity) {
this.activity = activity;
this.surfaceView = activity.getSurfaceView();
this.surfaceView.addOnPlayingFinishedListener(this);
}
@Override
public void run() {
try {
while(mediaList.size() == 0) {
Thread.sleep(1000);
}
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
playMedia(mediaList.get(currentVideo));
}
private void playMedia(String mediaPath) {
if(checkFileForImage(mediaPath)) {
surfaceView.drawImage(mediaPath, 2000);
} else {
surfaceView.playVideo(mediaPath);
}
}
private boolean checkFileForImage(String fileName) {
String file = fileName.toLowerCase();
return file.endsWith(".jpg") ||
file.endsWith(".img") ||
file.endsWith(".bmp") ||
file.endsWith(".jpeg") ||
file.endsWith(".ico") ||
file.endsWith(".tif");
}
@Override
public void onMediaPlayFinished() {
surfaceView.invalidate();
surfaceView.destroyDrawingCache();
currentVideo++;
if (currentVideo > mediaList.size() - 1) {
currentVideo = 0;
}
playMedia(mediaList.get(currentVideo));
}
}
不幸的是,这是预期的行为。由于 Android 中的一个问题,一旦您使用 Canvas 在 Surface 上绘制,您将无法执行任何其他操作。
你有三个选择:
- 使用 OpenGL ES 而非 Canvas 绘制图像。分离 GLES 后,您可以返回显示视频。
- 将图片呈现为单帧视频。这适用于一些琐碎的事情,比如让屏幕空白,但对于动态内容来说是一个糟糕的选择。
- 使用 FrameLayout 覆盖 ImageView 或自定义视图。当你想显示图像时显示视图,否则隐藏它。只要 Surface 处于默认的 Z 深度,ImageView 就会出现在它的前面并遮住它。