Android提供了多种视频播放的方式,如下:
1、MediaController+VideoView实现方式
这种方式是最简单的实现方式。VideoView继承了SurfaceView同时实现了MediaPlayerControl接口,MediaController则是安卓封装的辅助控制器,带有暂停,播放,停止,进度条等控件。通过VideoView+MediaController可以很轻松的实现视频播放、停止、快进、快退等功能。
2、MediaPlayer+SurfaceView+MediaController控制器或VideoView+自定义控制器
这种方式多少都方便一点,要求不高的时候可以使用。
3、MediaPlayer+SurfaceView+自定义控制器
这种方式最为复杂,但可以自由控制播放器的大小、位置以及各种事件,更为灵活。
本demo基于Android11开发,实现了简单的视频播放功能,可以拖动进度条,快进/快退和播放上一个视频/播放下一个视频,同时通过悬浮窗的形式查看视频详情。点击视频任意处即可暂停,播放中三秒不操作隐藏所有按钮。
之所以采用MediaPlayer+SurfaceView来播放是因为拖动进度条可以跳到最近的一帧,而VideoView只能跳到关键帧,但这种播放的实现方式更为复杂,具体步骤如下:
1、创建MediaPlayer对象,让它加载指定的视频文件。可以是应用的资源文件或本地文件路径。并添加setOnPreparedListener,setOnVideoSizeChangedListener和setOnCompletionListener三个监听分别用来监听视频装载完成事件,视频大小改变事件(屏幕适配)和视频播放完毕事件。
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
vseekBar.setProgress(0);
vgress.setText(Tool.millisToStringShort(0));//mediaplay的时间要进行处理
total=mediaPlayer.getDuration();
vtotal.setText(Tool.millisToStringShort(total));//mediaplay的时间要进行处理
vseekBar.setMax(total);
}
});
mediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() { //尺寸变化回调
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
changeVideoSize();
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
vseekBar.setProgress(0);
vpause.setVisibility(View.GONE);
vplay.setVisibility(View.VISIBLE);
relativeLayout1.setVisibility(View.VISIBLE);
relativeLayout2.setVisibility(View.VISIBLE);
}
});
2、在界面布局文件中定义SurfaceView组件,并为SurfaceView的SurfaceHolder添加Callback监听器。监听器中继承surfaceCreated方法,在该方法中调用MediaPlayer的setDisplay()和setDataSource()将所播放的视频图像输出到指定的SurfaceView组件并利用prepareAsync()方法装载流媒体文件。利用setOnTouchListener为surfaceView设置点击监听,用来控制按钮的隐藏和点击暂停/播放。
surfaceHolder=surfaceView.getHolder();
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
//当SurfaceView中Surface创建时回掉
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
mediaPlayer.reset();
try {
mediaPlayer.setDisplay(holder);
mediaPlayer.setDataSource(context, Uri.parse(vPath));
mediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
//当SurfaceView的大小发生改变时候触发该方法
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
}
//Surface销毁时回调
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
if (mediaPlayer!=null) {
mediaPlayer.stop();
mediaPlayer.release();
}
}
});
//设置 surfaceView点击监听
surfaceView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
relativeLayout1.setVisibility(View.VISIBLE);
relativeLayout2.setVisibility(View.VISIBLE);
vplay.setVisibility(View.VISIBLE);
vpause.setVisibility(View.GONE);
} else {
mediaPlayer.start();
relativeLayout1.setVisibility(View.GONE);
relativeLayout2.setVisibility(View.GONE);
vplay.setVisibility(View.GONE);
vpause.setVisibility(View.GONE);
}
break;
}
return true;
}
});
3、调用callback接口来完成Handler的实例化,视频播放时接受消息传入并同步更新进度条和视频。同时为Seekbar组件添加setOnSeekBarChangeListener监听,用以实现进度条的拖动事件。
Handler handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) { /
if(msg.what==1){
try {
nowtime=mediaPlayer.getCurrentPosition();
vgress.setText(Tool.millisToStringShort(nowtime));
vseekBar.setProgress(nowtime);
handler.sendEmptyMessageDelayed(1,10); //设置延迟
}catch (IllegalStateException e){
e.printStackTrace();
}
}
return false;
}
});
public SeekBar.OnSeekBarChangeListener seekBarChangeListener=new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (!mediaPlayer.isPlaying()){
mediaPlayer.seekTo(seekBar.getProgress(),MediaPlayer.SEEK_CLOSEST);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
handler.removeMessages(1);
mediaPlayer.pause();
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mediaPlayer.start();
handler.sendEmptyMessage(1);
}
};
4、为其他按钮设置点击事件,快进快退只需要对当前进度条的时间进行加减,3秒后不操作隐藏按钮要用到handler.postDelayed()来实现延时。左右切换需要先调用ContentResolver.query()查询本地所有的视频,然后找到当前播放视频在所有视频列表中的索引,再重新加载切换后的mediaplay。
//用Thread类会报错
handler.postDelayed(new Runnable() {
@Override
public void run() {
relativeLayout1.setVisibility(View.GONE);
relativeLayout2.setVisibility(View.GONE);
vpause.setVisibility(View.GONE);
vplay.setVisibility(View.GONE);
}
}, 3000);
public static ArrayList<DataValue> getVideo() {
Cursor cursor = null;
cursor = ContentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
null, null, null, MediaStore.Video.Media.DEFAULT_SORT_ORDER);
while (cursor.moveToNext()) {
String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA));// 路径
File file = new File(path);
if (file == null || !file.exists()) {
continue;
}
DataValue value = new DataValue();
value.fileName = file.getName();
if (file.getName().length()>=30){
String name=value.fileName;
String name1=name.substring(0,4);
int b=name.lastIndexOf(".");
String name2=name.substring(b,name.length());
String name3=name1+"*********"+name2;
value.fileName = name3;
}
value.filePath = path;
Videolist.add(value);
}
return Videolist;
}
5、将展示视频详情的布局及组件初始化,创建WindowManager对象并设置窗口对应的值,将设置好的值和布局添加进WindowManager对象。文章来源:https://www.toymoban.com/news/detail-761623.html
private void initInfoView() {
infoView = View.inflate(this, R.layout.information, null);
nameTv = infoView.findViewById(R.id.tv_name);
createTv = infoView.findViewById(R.id.tv_create);
sizeTv = infoView.findViewById(R.id.tv_size);
formatTv = infoView.findViewById(R.id.tv_format);
resolutionTv = infoView.findViewById(R.id.tv_resolution);
pathTv = infoView.findViewById(R.id.tv_path);
int index = vFile.getName().lastIndexOf(".");
if (index > 0) {
nameTv.setText("文件名: "+ vFile.getName().substring(0, index));
formatTv.setText("类型: "+ vFile.getName().substring(index));
} else {
nameTv.setText("文件名: "+ vFile.getName());
formatTv.setText("类型: 未知");
}
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
if(!switchlist){
createTv.setText("新建时间: 无法识别");
resolutionTv.setText("分辨率:无法识别");
sizeTv.setText("文件大小: 无法识别");
pathTv.setText("文件路径: "+ vPath);
}else {
createTv.setText("新建时间: "+ format.format(vFile.lastModified()));
MediaMetadataRetriever retr = new MediaMetadataRetriever();
retr.setDataSource(vPath);
String height = retr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); // 视频高度
String width = retr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); // 视频宽度
if (height!=null&&width!=null){
resolutionTv.setText("分辨率:"+width+"*"+height);
}else {
resolutionTv.setText("分辨率:无法识别");
}
sizeTv.setText("文件大小: "+ Tool.formatSize(vFile.length()));
pathTv.setText("文件路径: "+ vPath);
}
}
private void createInfoDialog() {
DisplayMetrics dm= getResources().getDisplayMetrics();
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
params.gravity = Gravity.TOP | Gravity.START;
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
params.width = (int) (dm.widthPixels*0.3);
params.height = (int) (dm.heightPixels*0.2);
params.x = dm.widthPixels;
params.y = (int) (dm.heightPixels*0.6);
params.format = PixelFormat.RGBA_8888;
windowManager.addView(infoView, params);
}
最后,MainActivity通过Intent将视频路径传给VideoActivity,因为是Android11,还要动态申请权限。文章来源地址https://www.toymoban.com/news/detail-761623.html
public class MainActivity extends AppCompatActivity {
public static Context context;
private EditText mvideofile;
private Button mfileplay,mexamplay;
private String file;
private Intent intentexam,intentfile;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
requestPermission();
context=getApplicationContext();
mexamplay=findViewById(R.id.exampleplay);
mexamplay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
intentexam=new Intent(context,VideoActivity.class);
String exfile= "android.resource://" + getPackageName() + "/" + R.raw.examvideo;
intentexam.putExtra("path",exfile);
intentexam.putExtra("switch",false);
startActivity(intentexam);
}
});
mvideofile=findViewById(R.id.videofile);
mfileplay=findViewById(R.id.fileplay);
mfileplay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
file=mvideofile.getText().toString();
intentfile = new Intent(context, VideoActivity.class);
intentfile.putExtra("path", file);
intentfile.putExtra("switch",true);
startActivity(intentfile);
}
});
}
private void requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) {
Toast.makeText(this, "未打开管理所有文件权限", Toast.LENGTH_SHORT).show();
Intent intentall = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intentall.setData(Uri.parse("package:" + this.getPackageName()));
startActivity(intentall);
}
}
if (!canDrawOverlays(this)) {
Toast.makeText(this, "未打开悬浮窗权限", Toast.LENGTH_SHORT).show();
Intent intentoverlay = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intentoverlay.setData(Uri.parse("package:" + BuildConfig.APPLICATION_ID));
startActivity(intentoverlay);
}
}
}
到了这里,关于Android MediaPlayer+SurfaceView+自定义控制器实现视频播放的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!