Android VideoView 單次播放簡單,但需順序播放多個視頻時,需監聽播放完成事件,在回調中啟動下一個視頻。核心是實現 MediaPlayer.OnCompletionListener。
public class VideoPlaylistActivity extends AppCompatActivity {
private VideoView videoView;
private int[] videoResIds = {R.raw.video1, R.raw.video2, R.raw.video3, R.raw.video4};
private int currentIndex = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_playlist);
videoView = findViewById(R.id.videoView);
playNext();
}
private void playNext() {
if (currentIndex >= videoResIds.length) {
// 播放結束,可選:循環、跳轉、釋放資源
currentIndex = 0; // 循環播放
// finish(); // 或結束 Activity
}
String uri = "android.resource://" + getPackageName() + "/" + videoResIds[currentIndex];
videoView.setVideoURI(Uri.parse(uri));
videoView.setOnCompletionListener(mp -> {
currentIndex++;
playNext();
});
videoView.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (videoView != null) {
videoView.stopPlayback(); // 釋放 MediaPlayer 資源
}
}
}
public class AdvancedVideoPlaylistActivity extends AppCompatActivity {
private VideoView videoView;
private ProgressBar loadingBar;
private List videoUrls = Arrays.asList(
"https://example.com/video1.mp4",
"https://example.com/video2.mp4",
"https://example.com/video3.mp4"
);
private int currentIndex = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_advanced_playlist);
videoView = findViewById(R.id.videoView);
loadingBar = findViewById(R.id.loadingBar);
videoView.setOnPreparedListener(mp -> loadingBar.setVisibility(View.GONE));
videoView.setOnErrorListener((mp, what, extra) -> {
loadingBar.setVisibility(View.GONE);
Toast.makeText(this, "播放失敗: " + what, Toast.LENGTH_SHORT).show();
playNext(); // 跳過錯誤視頻
return true; // 已處理錯誤
});
videoView.setOnCompletionListener(mp -> playNext());
playNext();
}
private void playNext() {
if (currentIndex >= videoUrls.size()) {
finish(); // 播放完畢
return;
}
loadingBar.setVisibility(View.VISIBLE);
videoView.setVideoPath(videoUrls.get(currentIndex++));
videoView.start();
}
}
<FrameLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black">
<VideoView
android:id="@+id/videoView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
<ProgressBar
android:id="@+id/loadingBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
</FrameLayout>
| 問題 | 解決方案 |
|---|---|
| 視頻切換黑屏/卡頓 | 預加載下一個視頻:用 MediaPlayer 直接管理、或雙 VideoView 交換 |
| 內存洩漏/ANR | onDestroy 中調用 stopPlayback(),釋放 MediaPlayer |
| 網絡視頻緩衝久 | 顯示 ProgressBar,setOnPreparedListener 隱藏 |
| 編碼格式不支援 | 捕獲 OnErrorListener,記錄日誌、跳過或降級 |
| 全屏/旋轉處理 | 鎖定橫屏 setRequestedOrientation,或用 ExoPlayer 支援更好 |
Google 官方推薦 Media3 ExoPlayer,支援:
// ExoPlayer 播放列表
MediaItem[] items = urls.stream().map(MediaItem::fromUri).toArray(MediaItem[]::new);
ConcatenatingMediaSource playlist = new ConcatenatingMediaSource(
Arrays.stream(items).map(item -> new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(item)).toArray(MediaSource[]::new)
);
player.setMediaSource(playlist);
player.prepare();
player.playWhenReady = true;
簡單需求用 VideoView 足夠;複雜場景(直播、DRM、廣告、離線)請直接上 Media3 ExoPlayer。