VideoView를 사용한 동영상 재생
Android 앱에서 동영상을 재생하는 대표적인 방법 중 하나는 VideoView 위젯을 활용하는 것입니다. 이 클래스는 SurfaceView를 상속하며, 간단한 API만으로 로컬 또는 네트워크상의 미디어 파일을 재생할 수 있도록 도와줍니다. 패키지 경로는 android.widget.VideoView이며, 개발자가 직접 Surface 관리나 코덱 처리를 할 필요 없이 손쉽게 동영상 플레이 기능을 구현할 수 있습니다.
주요 메서드 소개
- setVideoPath(String path): 로컬 경로에 있는 동영상 파일을 설정합니다.
- setVideoURI(Uri uri): URI 기반으로 동영상 소스를 지정합니다 (예: 인터넷 스트리밍).
- start(): 재생을 시작합니다.
- pause(): 일시 정지합니다.
- resume(): 일시 정지 후 다시 재생합니다.
- stopPlayback(): 재생을 완전히 중단하고 리소스를 해제합니다.
- seekTo(int msec): 특정 시간(ms 단위)으로 이동하여 재생합니다.
- getDuration(): 전체 재생 길이를 밀리초 단위로 반환합니다.
- getCurrentPosition(): 현재 재생 위치를 반환합니다.
- isPlaying(): 현재 재생 중인지 여부를 확인합니다.
- setMediaController(MediaController): 제어 바를 연결하여 사용자 인터페이스를 강화합니다.
- setOnPreparedListener(): 동영상 준비 완료 시 콜백을 받습니다.
- setOnCompletionListener(): 재생 종료 시 이벤트를 감지합니다.
- setOnErrorListener(): 오류 발생 시 처리할 수 있습니다.
VideoView는 내부적으로 MediaPlayer를 관리하므로 개별 초기화나 리소스 해제 코드가 필요 없습니다. start() 호출 시 자동으로 미디어를 로드하고 재생을 시작합니다.
기본 재생 예제
다음은 SD 카드에 저장된 동영상을 재생하고, 재생 상태를 제어하는 간단한 예제입니다.
레이아웃 파일: activity_video_player.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/edit_video_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="/sdcard/sample.mp4" />
<SeekBar
android:id="@+id/seekbar_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_start"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="시작" />
<Button
android:id="@+id/btn_pause"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="일시정지" />
<Button
android:id="@+id/btn_restart"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="다시재생" />
<Button
android:id="@+id/btn_stop"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="정지" />
</LinearLayout>
<VideoView
android:id="@+id/videoview_player"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
액티비티 코드: VideoPlayerActivity.java
package com.example.videoplayer;
import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.widget.*;
import java.io.File;
public class VideoPlayerActivity extends Activity {
private EditText editVideoPath;
private SeekBar seekBarProgress;
private Button btnStart, btnPause, btnRestart, btnStop;
private VideoView videoViewPlayer;
private boolean isPlaying = false;
private final Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_player);
initViews();
setupClickListeners();
setupSeekBar();
}
private void initViews() {
editVideoPath = findViewById(R.id.edit_video_path);
seekBarProgress = findViewById(R.id.seekbar_progress);
btnStart = findViewById(R.id.btn_start);
btnPause = findViewById(R.id.btn_pause);
btnRestart = findViewById(R.id.btn_restart);
btnStop = findViewById(R.id.btn_stop);
videoViewPlayer = findViewById(R.id.videoview_player);
}
private void setupClickListeners() {
btnStart.setOnClickListener(v -> playFrom(0));
btnPause.setOnClickListener(v -> togglePause());
btnRestart.setOnClickListener(v -> replay());
btnStop.setOnClickListener(v -> stopPlayback());
}
private void setupSeekBar() {
seekBarProgress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser && videoViewPlayer != null) {
videoViewPlayer.seekTo(progress);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
}
private void playFrom(int startPosition) {
String path = editVideoPath.getText().toString().trim();
File file = new File(path);
if (!file.exists()) {
Toast.makeText(this, "파일을 찾을 수 없습니다.", Toast.LENGTH_SHORT).show();
return;
}
videoViewPlayer.setVideoPath(file.getAbsolutePath());
videoViewPlayer.start();
videoViewPlayer.seekTo(startPosition);
seekBarProgress.setMax(videoViewPlayer.getDuration());
startProgressUpdater();
btnStart.setEnabled(false);
isPlaying = true;
videoViewPlayer.setOnCompletionListener(mp -> {
btnStart.setEnabled(true);
isPlaying = false;
});
videoViewPlayer.setOnErrorListener((mp, what, extra) -> {
Toast.makeText(this, "재생 오류", Toast.LENGTH_SHORT).show();
isPlaying = false;
return false;
});
}
private void startProgressUpdater() {
Runnable updateRunnable = new Runnable() {
@Override
public void run() {
if (isPlaying && videoViewPlayer != null && videoViewPlayer.isPlaying()) {
seekBarProgress.setProgress(videoViewPlayer.getCurrentPosition());
handler.postDelayed(this, 500);
}
}
};
handler.post(updateRunnable);
}
private void togglePause() {
if (videoViewPlayer.isPlaying()) {
videoViewPlayer.pause();
btnPause.setText("계속");
} else {
videoViewPlayer.start();
btnPause.setText("일시정지");
}
}
private void replay() {
if (videoViewPlayer != null) {
videoViewPlayer.seekTo(0);
videoViewPlayer.start();
}
}
private void stopPlayback() {
if (videoViewPlayer != null && videoViewPlayer.isPlaying()) {
videoViewPlayer.stopPlayback();
btnStart.setEnabled(true);
isPlaying = false;
}
}
}
MediaController와 연동하기
MediaController는 VideoView와 함께 사용되어 사용자 친화적인 제어 UI를 제공하는 클래스입니다. 기본 재생, 일시정지, 탐색, 진행률 표시 등을 포함한 풍부한 컨트롤 바를 화면에 띄울 수 있으며, 터치 입력 후 일정 시간이 지나면 자동으로 사라지는 오버레이 형태로 동작합니다.
MediaController의 주요 기능
- setMediaPlayer(MediaPlayerControl): 제어할 미디어 컴포넌트를 지정합니다. VideoView는 이 인터페이스를 구현합니다.
- setPrevNextListeners(): 이전/다음 동영상 전환 버튼에 리스너를 설정합니다. 등록하지 않으면 버튼이 나타나지 않습니다.
- isShowing(): 현재 컨트롤러가 화면에 표시되고 있는지 확인합니다.
MediaController 사용 예제
다음은 MediaController를 통해 UI 없이도 재생 제어를 가능하게 하는 예제입니다.
레이아웃: activity_media_controller.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<VideoView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
코드: MediaControllerActivity.java
package com.example.videoplayer;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.MediaController;
import android.widget.Toast;
import android.widget.VideoView;
import java.io.File;
public class MediaControllerActivity extends Activity {
private VideoView videoView;
private MediaController mediaController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_media_controller);
videoView = findViewById(R.id.video_view);
mediaController = new MediaController(this);
File videoFile = new File("/sdcard/sample.mp4");
if (videoFile.exists()) {
videoView.setVideoPath(videoFile.getAbsolutePath());
// 양방향 연결 필수
videoView.setMediaController(mediaController);
mediaController.setMediaPlayer(videoView);
// 이전/다음 버튼 리스너 추가 (옵션)
mediaController.setPrevNextListeners(
v -> Toast.makeText(this, "다음 영상", Toast.LENGTH_SHORT).show(),
v -> Toast.makeText(this, "이전 영상", Toast.LENGTH_SHORT).show()
);
} else {
Toast.makeText(this, "동영상 파일이 없습니다.", Toast.LENGTH_LONG).show();
}
}
}
결론
VideoView는 간단한 동영상 재생 기능을 빠르게 구현할 수 있는 강력한 도구입니다. 특히 MediaController와 조합하면 별도의 UI 개발 없이도 전문적인 플레이어 인터페이스를 제공할 수 있습니다. 하지만 고급 기능(자막 지원, 다양한 포맷 디코딩, 맞춤형 UI 등)이 필요한 경우, 더 많은 제어 권한을 제공하는 SurfaceView + MediaPlayer 조합이나 ExoPlayer 같은 외부 라이브러리 사용을 고려해야 합니다.