Android进阶之路

您所在的位置:网站首页 安卓原生屏幕录制怎么设置视频 Android进阶之路

Android进阶之路

2024-07-12 05:27| 来源: 网络整理| 查看: 265

在之前的项目中其实很少有用到VideoView的场景,只是去年看书的时候有看到音视频的一篇功能介绍,趁着最近有时间就学习整理了一下关于VideoView的blog ~

基础效果基础知识基础方法视频来源 开发实践视频控制器原始效果与自定义效果实现过程 功能扩展视频停止,释放资源保持屏幕常亮循环播放监听上一首、下一首切换功能监听视频是否播放完毕获取视频当前播放时长与视频总时长隐藏视频操作栏,如隐藏切换、快进、播放等功能重新设定快进、后退时间横竖屏适配长按快进、快退功能实践解决播放视频时,采用三方应用播放音乐,导致音视频声音并发的问题 问题锦集每次加载视频时,会黑屏一刹那无法加载视频,显示黑屏、'无法播放此视屏' 等视屏加载成功,但报出'无法播放此视屏'的弹框流量消耗巨大,出现流量损失

基础效果

其实实际效果比你当前看的效果要好,因为视频缓存过后的加载都比较快 ~ 在这里插入图片描述

基础知识

在Android中的视屏功能,大部分使用的都是VideoView控件,关于控制VideoView的方法,一般除了其本身自带方法以外,都搭配了MediaController操作视频,这里主要记录我学习的一个过程和结果 ~

VideoView控件引用

基础方法

VideoView、MediaController 常用方法

method含义start()开始播放视频pause()暂停播放视频resume()重新播放getDuration()获取视屏时长getCurrentPosition()获取当前已播放视频时长setVideoPath()设置视频文件路径setVideoURI()设置视频文件路径seekTo(int pos)滑动到指定播放进度isPlaying()判断视屏是否在播放canPause()是否禁用暂停按钮功能canSeekBackward()是否禁用滑动进度条功能canSeekForward()是否功能快进功能getAudioSessionId()获取音频会话IDsuspend()将VideoView所占用的资源释放掉

如何区别VideoView、MediaController的方法?

直接通过 MediaController 源码可以看到以下这些接口方法就都是 MediaController 的方法咯

在这里插入图片描述

视频来源

VideoView播放的视屏来源,主要有以下三种

本地内存(加载快,无卡顿,不过资源单一,如果要提速的话,可以将网络资源下载到本地) //加载本地视频,每个人存储地址不同,新手别抄 ~ File file = new File(Environment.getExternalStorageDirectory() + "/video.mp4"); if (file.exists()) { //设置视频地址 mVideo.setVideoPath(file.getAbsolutePath()); } else { Toast.makeText(this, "视频不存在", Toast.LENGTH_SHORT).show(); } 项目raw资源(加载快,无卡顿,不过资源单一) //加载项目内视频, "xxx": 视频名称(在res目录下新建raw,将xxx视频放在raw文件夹中) Uri uri = Uri.parse("android.resource://$packageName/${R.raw.xxx}"); mVideo.setVideoURI(uri); 网络加载(资源丰富,不过随着视频的大小,加载的时间也不尽相同,可以采用预加载优化用户体检验) //加载网络视频,记得适配 6.0,7.0,9.0 String videoPath = "https://vfx.mtime.cn/Video/2019/07/12/mp4/190712140656051701.mp4"; mVideo.setVideoPath(videoPath); 开发实践

此处仅为一个初级使用的小demo,会有部分思考的问题

注意

需自行适配6.0动态权限需自行适配7.0临时授权

AndroidMainfest 加入权限

MainActivity

package nk.com.video; import android.content.res.Configuration; import android.media.MediaPlayer; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.ImageButton; import android.widget.MediaController; import android.widget.Toast; import android.widget.VideoView; public class MainActivity extends AppCompatActivity { private VideoView mVideo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mVideo = findViewById(R.id.video); //网络加载 - 视屏地址 String videoPath = "https://vfx.mtime.cn/Video/2019/07/12/mp4/190712140656051701.mp4"; //视屏控制器 MediaController mediaController = new MediaController(MainActivity.this); //VideoView绑定控制器 mVideo.setMediaController(mediaController); //设置视频地址 mVideo.setVideoPath(videoPath); //获取焦点 mVideo.requestFocus(); //播放视频 mVideo.start(); } }

activity_main

视频控制器

在正式开发中很少有用到原始的MediaController控制View,一般都会定制化视图Controller ~

原始效果与自定义效果 MediaController自带的控制器 在这里插入图片描述效果 - 自定义Controller操作视图 在这里插入图片描述 实现过程 隐藏原始控制器 //不设置以下控制器属性 mVideo.setMediaController(mediaController); //如果已设置,则通过mediaController 隐藏操作栏 mediaController.setVisibility(View.GONE); 自定义控制器视图

这里我仅写了一个基础事件来进行效果展示,如果要写的完善一点可以参考原始封装的MediaController类

伪代码

//Video基础设置 tring videoPath = "https://media.w3.org/2010/05/sintel/trailer.mp4"; MediaController mediaController = new MediaController(MainActivity.this); mVideo.setVideoPath(videoPath); mVideo.requestFocus(); mVideo.start(); //自定义控制器功能 TextView pause = findViewById(R.id.pause_start); pause.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mVideo.isPlaying()){ mVideo.pause(); pause.setText("播放"); }else{ mVideo.start(); pause.setText("暂停"); } } });

伪代码

功能扩展

从 VideoView + 默认MediaController实现的功能来看,其主要覆盖视频播放、暂停、快进、后退、上一首、下一首的功能,这里首先讲一下这些带给我的思考 ~

Look here:我在解决VideoView相关功能时查看MediaController,发现MediaController本身就是系统封装好的一款自定义控件,所以遇到问题直接通过此类排查问题就行 ~

视频停止,释放资源

以下方法都有视频停止并且释放内存的作用,但又稍有不同

stopPlayback() 通过底层代码可以发现并没有重置,所以应该只是释放内存,并没有释放配置资源suspend() 则更加彻底的释放了所有配置信息和内存. //停止播放视频,并且释放 mVideoView.stopPlayback(); //在任何状态下释放媒体播放器 mVideoView.suspend(); 保持屏幕常亮

音视频开发的基本操作,在xml的根布局添加以下属性

android:keepScreenOn="true" 循环播放

关于音视频循环播放的方式,主要有以下三种

方式1:视频播放完成后,在onCompletion进行监听,重新播放视频

主要调用VideoView的resume()方法

mVideo.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { mVideo.resume(); } }); 方式2:通过MediaPlayer控制器实现循环播放 mp.setLooping(true);

这种方式 - 方法可用在OnPreparedListener加载回调和OnInfoListener视频信息回调,但不能用在OnCompletionListener播放完毕的回调中,在播放完毕时再调用此方法并不会让视频循环播放。

还有,设置了视频循环播放后,下一轮的播放不会再触发OnPreparedListener和OnInfoListener,但一样会触发在OnCompletionListener和异常回调。

方式3:这三种方式当视频重复播放时都会触发信息回调,与mp.setLooping(true)不太一样。 mVideo.setOnInfoListener(new MediaPlayer.OnInfoListener() { @Override public boolean onInfo(MediaPlayer mp, int what, int extra) { return false; } });

但VedioView.resume()方法还会触发加载状态回调OnPreparedListener,所以说resume()方法其实是再一次加载了这个视频内容,然后从头开始播放,与前二种方式是有所区别,前两种方式是再次播放已经加载好的视频,所以不会再触发OnPreparedListener这个回调。

监听上一首、下一首切换功能

主要调用MediaController控件封装的PrevNextListeners切换监听

mediaController.setPrevNextListeners(new View.OnClickListener() { @Override public void onClick(View v) { Log.e("tag", "下一首"); } }, new View.OnClickListener() { @Override public void onClick(View v) { Log.e("tag", "上一首"); } }); 监听视频是否播放完毕

VideoView提供了获取视频当前播放时长、总时长,以及状态监听的功能

mVideo.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { Log.e("tag","播放完成"); } }); 获取视频当前播放时长与视频总时长

主要涉及getCurrentPosition、getDuration俩个方法,其实在基础部分都有讲明 ~

int currentPosition = mVideo.getCurrentPosition(); Log.e("tag", "当前时长:" + currentPosition); int duration = mVideo.getDuration(); Log.e("tag", "视频时长:" + duration); 隐藏视频操作栏,如隐藏切换、快进、播放等功能

方式一:VideoView不调用setMediaController()即可

在这里插入图片描述

方式二:MediaController本身就是自定义控件,直接用setVisibility()即可

//隐藏操作栏 mediaController.setVisibility(View.GONE);

示例

在这里插入图片描述

重新设定快进、后退时间

嗯哼,我找了挺多blog发现都没有人讲这方面的功能,我尝试在 MediaController 找重写快进、后退的方法,发现并没有... 后来发现源码中直接把快进时间和后退时间写固定了,具体如下

MediaController 关于快进功能的实现

在这里插入图片描述

MediaController 关于后退功能的实现

在这里插入图片描述

好吧,既然不支持重写,我想了俩种方法,不过这俩种方式最终没有实现快进、后退的时间修改,仅作为思想延伸,想法扩展的记录

方式一(无效):自己往 MediaController 里面加一加重写快进、后退的代码(因为没锁,所以可编译)

在这里插入图片描述

虽然在原始代码中加了设置方式,但是系统压根不识别,所以此路无效

在这里插入图片描述

方式二(无效):因为第一种方法无效,所以我只能copy出原始MediaController,然后自己改改咯(copy后有很多报错,首先要更改调用的包名,其次删除部分无用功能,我们主要尝试去重写快进、后退方法)

在这里插入图片描述

在上方我们已经写好我们的视频控制类了,然后然后… 什么鬼!白写!!!系统压根不认 ~ 无语,当然我们还有一种方式,就是弃用他的控制类,我们自己写一套操作视频的布局和功能(自定义控件也行 ~)

在这里插入图片描述

横竖屏适配

很多视频相关功能都支持用户横竖屏观看,根据以下设置后,我发现横竖屏并不会影响到视频重播

常规思路:监听用户横竖屏改变时,记录用户视频的播放进度,当方向转换后,重新将视频播放进度设置到之前记录的播放进度处

考虑问题

横竖屏监听视频播放进度记录与设置

视频适配

修改承载横竖屏Activity的configChanges属性,如下 android:configChanges="orientation|keyboard|layoutDirection|screenSize"

示例

对应Activity内重写onConfigurationChanged监听屏幕方向的改变 @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { Toast.makeText(getApplicationContext(), "横屏", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getApplicationContext(), "竖屏", Toast.LENGTH_SHORT).show(); } } 长按快进、快退功能实践

常规的长按事件处理,只能触发一次,即点击一次,快进一次(不适用于当前场景)

implements View.OnLongClickListener, mPreviousView.setOnLongClickListener(this) @Override public boolean onLongClick(View view) { if (view == mNextView) { if (null != mPlaylistListener) { Log.d("hrl", "onLongClick"); mPlaylistListener.onFastPlay(); return true; } } if (view == mPreviousView) { if (null != mPlaylistListener) { Log.d("hrl", "onLongClick"); mPlaylistListener.onBackPlay(); return true; } } return false; }

适用方案 - 自定义VideoView中加入以下设置(不可完全copy,需要有选择的借鉴)

private int mLastMotionX, mLastMotionY; //是否移动了 private boolean isMoved; //长按的runnable private Runnable mLongPressFastRunnable = new Runnable() { @Override public void run() { //调用快进函数 mPlaylistListener.onFastPlay(); Log.d("hrl", "mLongPressFastRunnable"); //若是postDelay(),会发生左右摇摆的现象,原因是获取当前位置的时候会出现延时,使得俩次postDelay获取到的pos一致。 post(this); } }; private Runnable mLongPressBackRunnable = new Runnable() { @Override public void run() { mPlaylistListener.onBackPlay(); Log.d("hrl", "mLongPressBackRunnable"); post(this); } }; //移动的阈值 private static final int TOUCH_SLOP = 20; private boolean longPress = false; @Override public boolean onTouch(View view, MotionEvent event) { if (view == mNextView) { int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d("hrl", "mNextView ACTION_DOWN"); mLastMotionX = x; mLastMotionY = y; isMoved = false; postDelayed(mLongPressFastRunnable, ViewConfiguration.getLongPressTimeout()); break; case MotionEvent.ACTION_MOVE: Log.d("hrl", "mNextView ACTION_MOVE"); if (isMoved) break; if (Math.abs(mLastMotionX - x) > TOUCH_SLOP || Math.abs(mLastMotionY - y) > TOUCH_SLOP) { //移动超过阈值,则表示移动了 isMoved = true; } break; case MotionEvent.ACTION_UP: Log.d("hrl", "mNextView ACTION_UP"); removeCallbacks(mLongPressFastRunnable); break; } } if (view == mPreviousView) { int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d("hrl", "mNextView ACTION_DOWN"); mLastMotionX = x; mLastMotionY = y; isMoved = false; postDelayed(mLongPressBackRunnable, ViewConfiguration.getLongPressTimeout()); break; case MotionEvent.ACTION_MOVE: Log.d("hrl", "mNextView ACTION_MOVE"); if (isMoved) break; if (Math.abs(mLastMotionX - x) > TOUCH_SLOP || Math.abs(mLastMotionY - y) > TOUCH_SLOP) { //移动超过阈值,则表示移动了 isMoved = true; } break; case MotionEvent.ACTION_UP: Log.d("hrl", "mNextView ACTION_UP"); removeCallbacks(mLongPressBackRunnable); break; } } return false; } 解决播放视频时,采用三方应用播放音乐,导致音视频声音并发的问题

借鉴的2016年以为前辈的blog,具体效果未亲自尝试

解决音视频并发问题,可以在自定义的xxxVideoView中或视频播放的xxxActivity中添加如下代码;

//用AudioManager获取音频焦点避免音视频声音并发问题 private AudioManager mAudioManager; private OnAudioFocusChangeListener mAudioFocusChangeListener; //在播放视频的时候请求音频焦点,第三方应用在失去音频焦点后会暂停播放(音视频应用一般都会遵守音频焦点机制,在失去焦点的回调中做暂停等处理); @Override public void start() { if (requestTheAudioFocus() == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { //焦点获取成功,播放操作 }else { //提示用户关闭其他音频再播放,不然用户以为是bug呢... } } //在暂停视频、播放完成或退到后台时释放音频焦点; @Override public void pause() { releaseTheAudioFocus(mAudioFocusChangeListener); //暂停逻辑 }

请求音频焦点,并设置监听器

//请求音频焦点 设置监听 private int requestTheAudioFocus() { if (Build.VERSION.SDK_INT


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3