前言

  VideoView是Android主要的视频播放View,它其实是对MediaPlayer的再次封装.如果你已经了解过MediaPlayer在使用VideoView是十分简单的.如果你想先了解MediaPlayer可以参考我的博客:https://www.cnblogs.com/guanxinjing/p/11019662.html

在没有复杂的要求下使用VideoView播放视频是十分快速且方便的选择.并且不需要苦恼视频尺寸的计算(说到视频尺寸计算,个人瞎折腾出了一个计算方法.虽然也可以将视频适配的很完美,但是总的还是没有VideoView内置的视频尺寸适配厉害,有兴趣的可以阅读一下源代码里VideoView的视频适配,这个才是精华部分...)

实现流程

  •   获取权限
  •   保持屏幕常亮
  •   xml布局里添加VideoView
  •   初始化配置VideoView
  •   播放视频
  •   暂停视频
  •   获取播放时间信息
  •   停止视频

获取权限

  这个是播放本地视频的demo,所以只添加了需要SD卡权限.另外VideoView是支持网络视频播放的如果你需要播放网络视频则还需要添加网络权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

保持屏幕常亮

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

android:keepScreenOn="true"

xml布局里添加VideoView

<VideoView
android:id="@+id/video_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>

基本操作,但是这里有一个你可能会忽视的VideoView的高宽配置.了解这个你可以看出VideoView自带的视频尺寸适配还是十分完美的.

1.第一种当你android:layout_width="wrap_content" 和 android:layout_height="wrap_content" 都设置为wrap_content时,这个时候View适配视频是一个在当前设备屏幕下最合适的一个比例. 虽然比例是最合适的但是View宽和高并不会一定铺满屏幕.这种方式适合在一些悬浮的小窗口播放时使用.

2.第二种就是满足屏幕的一边,满足屏幕的一边的意思是让VideoView始终铺满设备屏幕的宽或者高, 这里有一个原则就是始终满足最小的那个一个边.

我们在竖屏的情况下首先要满足宽,所以属性应该配置成    android:layout_width="match_parent"           android:layout_height="wrap_content"

反之如果我们在横屏的情况就要满足高度.所以属性应该配置成    android:layout_width="wrap_content"           android:layout_height="match_parent"

这样我们的视频才不会变形拉伸.重点!这种方式是我们最适用在视频播放时的使用.(如果你是动态切换横竖屏,我们也可以在代码里重新设置VideoView的layout_width和layout_height)

3.第三种 layout_width和layout_height属性都是match_parent,这个将无法避免视频的变形拉伸,不建议这样配置.

4.第四种 layout_width和layout_height属性都自己设置固定大小的值.这个也将无法避免视频的变形拉伸.如果,你还是一定需要用小窗口播放,这里可以给出一个思路:

就是在VideoView外面在套一个小窗口的黑色背景View,而你只要遵守第二条情况满足这小窗口的黑色背景View的最短的一边.就可以完美的适配视频了.按自动比例缩小视频,其他地方会变成电影一样的黑边效果.布局参考如下:

<View
android:id="@+id/video_bg"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="@color/fontBlack1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/> <VideoView
android:id="@+id/video_view2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="@id/video_bg"
app:layout_constraintBottom_toBottomOf="@id/video_bg"
app:layout_constraintLeft_toLeftOf="@id/video_bg"
app:layout_constraintRight_toRightOf="@id/video_bg"/>

初始化配置VideoView

private void initVideoView(){
File file = new File(getExternalCacheDir(),"demo.mp4");
if (!file.exists()){
Toast.makeText(this, "视频不存在", Toast.LENGTH_SHORT).show();
finish();
return;
}
mVideoView.setVideoPath(file.getAbsolutePath());//设置视频文件
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
//视频加载完成,准备好播放视频的回调 }
});
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
//视频播放完成后的回调 }
});
mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
//异常回调
return false;//如果方法处理了错误,则为true;否则为false。返回false或根本没有OnErrorListener,将导致调用OnCompletionListener。
}
});
mVideoView.setOnInfoListener(new MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
//信息回调
// what 对应返回的值如下
// public static final int MEDIA_INFO_UNKNOWN = 1; 媒体信息未知
// public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; 媒体信息视频跟踪滞后
// public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; 媒体信息\视频渲染\开始
// public static final int MEDIA_INFO_BUFFERING_START = 701; 媒体信息缓冲启动
// public static final int MEDIA_INFO_BUFFERING_END = 702; 媒体信息缓冲结束
// public static final int MEDIA_INFO_NETWORK_BANDWIDTH = 703; 媒体信息网络带宽(703)
// public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; 媒体-信息-坏-交错
// public static final int MEDIA_INFO_NOT_SEEKABLE = 801; 媒体信息找不到
// public static final int MEDIA_INFO_METADATA_UPDATE = 802; 媒体信息元数据更新
// public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; 媒体信息不支持字幕
// public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; 媒体信息字幕超时 return false; //如果方法处理了信息,则为true;如果没有,则为false。返回false或根本没有OnInfoListener,将导致丢弃该信息。
}
});
}

播放视频

mVideoView.start();

注意,播放视频时一定要确定setOnPreparedListener返回了已经视频准备完成的回调.

暂停视频

mVideoView.pause();

获取播放时间信息

mVideoView.getDuration();//获取视频的总时长
mVideoView.getCurrentPosition();//获取视频的当前播放位置

这2个方法本质上还是调用MediaPlayer获取的,获取到时长后你可以配合Handler与SeekBar配合来实现自定义进度条功能.这里就不在啰嗦了.有兴趣可以参考我的一个简单的demo(虽然demo里是用MediaPlayer实现,但是原理一致):https://www.cnblogs.com/guanxinjing/p/11024195.html

停止释放

mVideoView.stopPlayback();//停止播放视频,并且释放
mVideoView.suspend();//在任何状态下释放媒体播放器
两个方法都有停止并且释放内存的作用,但是stopPlayback()看底层代码并没有重置,所以应该只是释放内存,但是没有释放配置资源(但是想不在配一次视频路径应该是需要调用mMediaPlayer.prepareAsync();方法,看不懂这个?可以参考我MediaPlayer入门的博客). 
而suspend则更加彻底的释放了所有配置信息和内存.

其他Api介绍

  • mVideoView.canPause();  //是否可以暂停
  • mVideoView.canSeekBackward();  //视频是否可以向后调整播放位置
  • mVideoView.canSeekForward();  //视频是否可以向前调整播放位置
  • mVideoView.getBufferPercentage();  //获取视频缓冲百分比
  • mVideoView.resolveAdjustedSize();  //获取自动解析后VideoView的大小
  • mVideoView.resume();  //重新开始播放
  • mVideoView.isPlaying();  //是否在播放中
  • mVideoView.setMediaController();  //设置多媒体控制器
  • mVideoView.onKeyDown();  //发送物理按键值
  • mVideoView.setVideoURI();   //播放网络视频,参数为网络地址

end

Android开发 VideoView视频播放详解的更多相关文章

  1. Android开发–Intent-filter属性详解

    Android开发–Intent-filter属性详解 2011年05月09日 ⁄ Andriod ⁄ 暂无评论 ⁄ 被围观 1,396 views+ 如果一个 Intent 请求在一片数据上执行一个 ...

  2. 搭建Android开发环境附图详解+模拟器安装(JDK+Eclipse+SDK+ADT)

    ——搭建android开发环境的方式有多种,比如:JDK+Eclipse+SDK+ADT或者JDK+Eclipse+捆绑好的AndroidSDK或者Android Studio. Google 决定将 ...

  3. android 开发 View _6_Canvas详解

    牛逼大神的博客地址:http://www.gcssloop.com/customview/Canvas_BasicGraphics 安卓自定义View进阶-Canvas之绘制图形 在上一篇自定义Vie ...

  4. Android 开发 之 Fragment 详解

    本文转载于 : http://blog.csdn.net/shulianghan/article/details/38064191 本博客代码地址 : -- 单一 Fragment 示例 : http ...

  5. 2-5 Flutter开发环境与Android开发环境设置详解(Windows)

    第二个是国内服务器的网址 andoid stuido的一些使用的说明文档 https://developer.android.google.cn/studio/intro 安装Flutter Dart ...

  6. Android 开发之动画详解

    一.动画类型 Android的animation由四种类型组成:alpha.scale.translate.rotate XML配置文件中 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画 ...

  7. android 开发 对话框Dialog详解

    转载请注明出处:红亮的专栏:http://blog.csdn.net/liang5630/article/details/44098899 Android中的对话框形式大致可分为五种:分别是一般对话框 ...

  8. Android开发 DialogFragment对话框详解

    前言 在聊DialogFragment之前,我们看看以往我们在Android里实现一个对话框一般有这几种方式: Dialog 继承重写Dialog实现一个自定义的Dialog AlertDialog ...

  9. Android开发 navigation入门详解

    前言 Google 在2018年推出了 Android Jetpack,在Jetpack里有一种管理fragment的新架构模式,那就是navigation. 字面意思是导航,但是除了做APP引导页面 ...

随机推荐

  1. 西里尔字 俄语 - Cyrillic

    https://zh.wikipedia.org/wiki/%E8%A5%BF%E9%87%8C%E5%B0%94%E5%AD%97%E6%AF%8D 其他编码[编辑] 其他适用西里尔字母的字符编码系 ...

  2. Codeforces 360C DP 计算贡献

    题意:给你一个长度为n的字符串,定义两个字符串的相关度为两个串对应的子串中第一个串字典序大于第二个串的个数.现在给你相关度,和第二个串,问满足条件的第一个串有多少个? 思路:设dp[i][j]为填了前 ...

  3. 每天一个Linux命令:rm(5)

    rm rm命令可以删除一个目录中的一个或多个文件或目录,也可以将某个目录及其下属的所有文件及其子目录均删除掉.对于链接文件,只是删除整个链接文件,而原有文件保持不变 注意:使用rm命令要格外小心.因为 ...

  4. 为什么不能在shell脚本中执行source /etc/profile或者source ~/.bashrc问题?

    执行脚本时,其中的命令是在一个子shell中执行的.子shell继承了父shell的环境变量,但无法修改他们,或者说所做的修改仅对子shell有效.

  5. 【Flutter学习】基本组件之基本滑动PageView组件

    一,概述 PageView 是一个滑动视图列表,它也是继承至 CustomScrollView 的. 二,构造函数 类命构造函数(PageView) PageView 使用场景:创建一个可滚动列表 构 ...

  6. Python基础(二):斐波那契数列、模拟cp操作、生成8位随机密码

    一.斐波那契数列 目标: 编写fib.py脚本,主要要求如下: 输出具有10个数字的斐波那契数列 使用for循环和range函数完成 改进程序,要求用户输入一个数字,可以生成用户需要长度的斐波那契数列 ...

  7. BZOJ 4059: [Cerc2012]Non-boring sequences(启发式分治)

    传送门 解题思路 首先可以想到要预处理一个\(nxt_i\)和\(pre_i\),表示前后与当前位置权值相同的节点,那么这样可以迅速算出某个点在某段区间是否出现多次.然后这样的话就考虑分治,对于\([ ...

  8. VMware Hyper-V不兼容

    VMware Workstation Windows系統的Hyper-V不相容 禁用Device Guard或Credential Guard 1. 以管理員身份運行Windows Powershel ...

  9. SqlSession(SqlSessionTemplate类) 实现Mybatis

    yBatis3与spring整合之使用SqlSession(SqlSessionDaoTemplate类) ---------- 注:这是手工编写实现的方式(其实可以直接使用注入映射器的) SqlSe ...

  10. Red Hat Enterprise Linux 7.x新特性

    Red Hat Enterprise Linux 7.x新特性 RHEL7新特性简介 1.      RHEL7目前支持架构 64-bit AMD.64-bit Intel.IBM POWER.IBM ...