知乎專欄 | 多維度架構 | | | 微信號 netkiller-ebook | | | QQ群:128659835 請註明“讀者” |
VideoView,用於播放一段視頻媒體,它繼承了SurfaceView,位於"android.widget.VideoView",是一個視頻控件。
VideoView也為開發人員提供了對應的方法,這裡簡單介紹一些常用的: int getCurrentPosition():獲取當前播放的位置。 int getDuration():獲取當前播放視頻的總長度。 isPlaying():當前VideoView是否在播放視頻。 void pause():暫停 void seekTo(int msec):從第幾毫秒開始播放。 void resume():重新播放。 void setVideoPath(String path):以檔案路徑的方式設置VideoView播放的視頻源。 void setVideoURI(Uri uri):以Uri的方式設置VideoView播放的視頻源,可以是網絡Uri或本地Uri。 void start():開始播放。 void stopPlayback():停止播放。 setMediaController(MediaController controller):設置MediaController控製器。 setOnCompletionListener(MediaPlayer.onCompletionListener l):監聽播放完成的事件。 setOnErrorListener(MediaPlayer.OnErrorListener l):監聽播放發生錯誤時候的事件。 setOnPreparedListener(MediaPlayer.OnPreparedListener l)::監聽視頻裝載完成的事件。
上面的一些方法通過方法名就可以瞭解用途。和MediaPlayer配合SurfaceView播放視頻不同,VideoView播放之前無需編碼裝載視頻,它會在start()開始播放的時候自動裝載視頻。並且VideoView在使用完之後,無需編碼回收資源。
加入 android.permission.INTERNET 允許訪問網絡
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.netkiller.video"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".VideoViewActivity"></activity> </application> </manifest>
最簡潔的佈局,只有一個 VideoView
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".VideoViewActivity"> <VideoView android:id="@+id/videoView" android:layout_width="match_parent" android:layout_height="236dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </android.support.constraint.ConstraintLayout>
播放的檔案來自 IPFS 星際檔案系統
package cn.netkiller.video; import android.net.Uri; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.MediaController; import android.widget.VideoView; public class VideoViewActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_view); Uri uri = Uri.parse("http://ipfs.netkiller.cn/ipfs/QmcA1Fsrt6jGTVqAUNZBqaprMEdFaFkmkzA5s2M6mF85UC"); VideoView videoView = (VideoView) this.findViewById(R.id.videoView); videoView.setMediaController(new MediaController(this)); videoView.setVideoURI(uri); videoView.start(); videoView.requestFocus(); } }
運行程序開始播放視頻,點擊視頻會在屏幕下方彈出 MediaController 控制條
package cn.netkiller.video; import android.net.Uri; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.MediaController; import android.widget.Toast; import android.widget.VideoView; public class VideoViewActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_view); final Uri uri = Uri.parse("http://ipfs.netkiller.cn/ipfs/QmcA1Fsrt6jGTVqAUNZBqaprMEdFaFkmkzA5s2M6mF85UC"); final VideoView videoView = (VideoView) this.findViewById(R.id.videoView); MediaController mediaController = new MediaController(this); mediaController.setMediaPlayer(videoView); mediaController.setPrevNextListeners( new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(VideoViewActivity.this, "下一曲", Toast.LENGTH_SHORT).show(); videoView.setVideoURI(Uri.parse("http://ipfs.netkiller.cn/ipfs/QmUaDftnPB7zCTwTASnSAWLiXWd1L5vNGEeU585rddfVTh")); } }, new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(VideoViewActivity.this, "上一曲", Toast.LENGTH_SHORT).show(); videoView.setVideoURI(Uri.parse("http://ipfs.netkiller.cn/ipfs/QmbvKvj9X368WMtmkLYFuf59gSwLXYDLcdJuSiSHKPhTG4")); } }); videoView.setMediaController(mediaController); videoView.setVideoURI(uri); videoView.start(); videoView.requestFocus(); } }
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mediaPlayer) { mediaPlayer.setVolume(0f, 0f); } });
new Thread() { @Override public void run() { try { while (videoView.isPlaying()) { // 如果正在播放,沒0.5.毫秒更新一次進度條 int current = videoView.getCurrentPosition(); seekBar.setProgress(current); sleep(500); } } catch (Exception e) { e.printStackTrace(); } } }.start();
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".VideoViewActivity"> <VideoView android:id="@+id/videoView" android:layout_width="match_parent" android:layout_height="236dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/textViewTime" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="1" android:text="00:00" /> <SeekBar android:id="@+id/seekBar" android:layout_width="270dp" android:layout_height="wrap_content" /> <TextView android:id="@+id/textViewCurrentPosition" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="00:00" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/buttonPlay" android:layout_width="183dp" android:layout_height="wrap_content" android:text="播放" /> <Button android:id="@+id/buttonStop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="停止" /> </LinearLayout> </LinearLayout> <TextView android:id="@+id/textViewStatus" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:text=" " app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/videoView" /> </android.support.constraint.ConstraintLayout>
package cn.netkiller.video; import android.media.MediaPlayer; import android.net.Uri; import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import android.widget.VideoView; import java.text.SimpleDateFormat; import java.util.Calendar; public class VideoViewActivity extends AppCompatActivity implements View.OnClickListener { private VideoView videoView; private SeekBar seekBar; private Button buttonPlay; private TextView textViewTime; private TextView textViewCurrentPosition; private TextView textViewStatus; private Handler handler = new Handler(); private Runnable runnable = new Runnable() { public void run() { if (videoView.isPlaying()) { int current = videoView.getCurrentPosition(); seekBar.setProgress(current); textViewCurrentPosition.setText(time(videoView.getCurrentPosition())); } handler.postDelayed(runnable, 500); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_view); final Uri uri = Uri.parse("http://ipfs.netkiller.cn/ipfs/QmcA1Fsrt6jGTVqAUNZBqaprMEdFaFkmkzA5s2M6mF85UC"); videoView = (VideoView) this.findViewById(R.id.videoView); videoView.setVideoURI(uri); videoView.requestFocus(); videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mediaPlayer) { textViewTime.setText(time(videoView.getDuration())); textViewStatus.setText("視頻加載完畢"); buttonPlay.setEnabled(true); } }); // 在播放完畢被回調 videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { Toast.makeText(VideoViewActivity.this, "播放完成", Toast.LENGTH_SHORT).show(); } }); videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { // 發生錯誤重新播放 play(); Toast.makeText(VideoViewActivity.this, "播放出錯", Toast.LENGTH_SHORT).show(); return false; } }); textViewStatus = (TextView) findViewById(R.id.textViewStatus); textViewStatus.setText("玩命加載中"); textViewTime = (TextView) findViewById(R.id.textViewTime); seekBar = (SeekBar) findViewById(R.id.seekBar); // 為進度條添加進度更改事件 seekBar.setOnSeekBarChangeListener(onSeekBarChangeListener); textViewCurrentPosition = (TextView) findViewById(R.id.textViewCurrentPosition); buttonPlay = (Button) findViewById(R.id.buttonPlay); buttonPlay.setEnabled(false); final Button buttonStop = (Button) findViewById(R.id.buttonStop); buttonPlay.setOnClickListener(this); buttonStop.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.buttonPlay: play(); break; case R.id.buttonStop: stop(); break; default: break; } } private SeekBar.OnSeekBarChangeListener onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() { // 當進度條停止修改的時候觸發 @Override public void onStopTrackingTouch(SeekBar seekBar) { // 取得當前進度條的刻度 int progress = seekBar.getProgress(); if (videoView.isPlaying()) { // 設置當前播放的位置 videoView.seekTo(progress); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } }; protected void play() { if (buttonPlay.getText().equals("播放")) { buttonPlay.setText("暫停"); textViewStatus.setText("請您欣賞"); // 開始綫程,更新進度條的刻度 handler.postDelayed(runnable, 0); videoView.start(); seekBar.setMax(videoView.getDuration()); } else { buttonPlay.setText("播放"); if (videoView.isPlaying()) { videoView.pause(); } } } protected void stop() { if (videoView.isPlaying()) { videoView.stopPlayback(); } } protected String time(long millionSeconds) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss"); Calendar c = Calendar.getInstance(); c.setTimeInMillis(millionSeconds); return simpleDateFormat.format(c.getTime()); } @Override protected void onDestroy() { super.onDestroy(); handler.removeCallbacks(runnable); } }