用原生VideoView进行全屏播放时的问题
之前参加了一个课程,里面有一节讲到了用视频作为启动界面。讲师用的是自定义VideoView,重写onMeasure方法,因为原生的VideoView在那情况下不能实现全屏播放。当时没有深入研究,现在补回来。
用的是36氪之前的视频(608×1080)和Genymotion中的Google Nexus 5(1080×1920)。
一、效果图
1、原生VideoView的效果,这里没有让底部的导航栏也变透明。因为截图上来很难看到差别,后面会解释。
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
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"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"/> </LinearLayout>
java
public class VideoViewActivity extends AppCompatActivity { private VideoView mVideoView; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_view); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
} mVideoView = (VideoView) findViewById(R.id.video_view);
mVideoView.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.kr36));
mVideoView.start();
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mVideoView.start();
}
});
}
}
2、自定义的VideoView
布局文件基本同上,除了控件名和id
... <com.example.test.test_fitstatusbar.CustomVideoView
android:id="@+id/custom_video_view"
...
Activity.java也是基本同上。这里是自定义VideoView的java代码,只重写了onMeasure方法。
public class CustomVideoView extends VideoView { public CustomVideoView(Context context) {
super(context);
} public CustomVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
} public CustomVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = getDefaultSize(0, widthMeasureSpec);
int height = getDefaultSize(0, heightMeasureSpec); setMeasuredDimension(width, height);
}
}
二、在对比原生VideoView的onMeasure方法之前,先了解一些事情。
1、这里涉及到MeasureSpec类,这个类代码不多,但很精妙。我也有很多地方没弄懂。不过在这里,只需了解它的三种mode就可以了。
/**
* 1、UNSPECIFIED
* 根据源码的注释,其大概意思是parent不对child做出限制.它想要什么size就给什么size.看了一些教程,都说用得很少,或者是系统内部才用得上.所以先不管了
* 2、EXACTLY
* 对应于match_parent和给出具体的数值
* 3、AT_MOST
* 对应于wrap_content
*/
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT; public static final int UNSPECIFIED = 0 << MODE_SHIFT; public static final int EXACTLY = 1 << MODE_SHIFT; public static final int AT_MOST = 2 << MODE_SHIFT; ......
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
} ......
}
而这里,我所有控件的width和height都是mach_parent,所以以下分析都是基于MeasureSpec.EXACTLY这个mode。
2、getDefaultSize
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
因为都是MeasureSpec.EXACTLY,所以最终返回的结果是MeasureSpec.getSize(measureSpec),与size,也就是第一个参数无关。
3、setMeasuredDimension
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom; measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
中间的判断语句,涉及到ViewGroup的LayoutMode,它有两个值,一个是默认值clipBounds,效果就是保留子view之间的空白,因为有些控件看上去要比实际的小,但它仍然是占了给定的大小,只是系统让它的一部分边缘变成留白,这样的话,不至于子view真的是连接在一起;另一个是opticalBounds,它就是用来消除clipBounds的效果。不过,这种情况只在Holo Theme下才能有效。可以参考官方文档https://developer.android.com/about/versions/android-4.3.html#UI。但现在应该是用AppCompat Theme的比较多,至于怎么在AppCompat Theme下也实现这种效果,还有待解决。
一般情况下,都不会进入判断语句块里。
而这里要关注的其实是最后一句代码,setMeasuredDimensionRaw。
4、setMeasuredDimensionRaw
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight; mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
这个方法就是将最终的测量结果赋值给对应的view的全局变量,意味着measure部分结束。
三、对比原生VideoView的onMeasure方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
// + MeasureSpec.toString(heightMeasureSpec) + ")"); int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
int height = getDefaultSize(mVideoHeight, heightMeasureSpec); if (mVideoWidth > 0 && mVideoHeight > 0) { int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
// the size is fixed
width = widthSpecSize;
height = heightSpecSize; // for compatibility, we adjust size based on aspect ratio
if ( mVideoWidth * height < width * mVideoHeight ) {
//Log.i("@@@", "image too wide, correcting");
width = height * mVideoWidth / mVideoHeight;
} else if ( mVideoWidth * height > width * mVideoHeight ) {
//Log.i("@@@", "image too tall, correcting");
height = width * mVideoHeight / mVideoWidth;
}
} else if (widthSpecMode == MeasureSpec.EXACTLY) {
......
} else if (heightSpecMode == MeasureSpec.EXACTLY) {
......
} else {
......
}
} else {
// no size yet, just adopt the given spec sizes
}
setMeasuredDimension(width, height);
}
为了方便对比,再贴出onMeasure方法。我在这个方法中,打印过width和height的值,它们的值就是屏幕显示部分的分辨率。意思是说,按这里的情况来讲,当状态栏和底部的导航栏都是透明时,width是1080,height是1920,正好是Google Nexus 5的分辨率。
当底部的导航栏不是透明时,height就是1776。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = getDefaultSize(0, widthMeasureSpec);
int height = getDefaultSize(0, heightMeasureSpec); setMeasuredDimension(width, height);
}
现在对比原生的onMeasure方法来分析。
首先是通过getDefaultSize来得到width和height。上面说过,在我这个例子中,getDefaultSize的返回值只与第二个参数有关,即widthMeasureSpec和heightMeasureSpec,而这两个参数都是从相同的ViewGroup传进来的,所以无论是原生还是重写,其从getDefaultSize中得到的值都是一样的。然后进入第一层判断语句块,在这里通过MeasureSpec.getMode()和getSize(),再次取得控件的mode和size。其实这在getDefaultSize里也有实现,所以外层的width和widthSpecSize的值是相同的,height也是这种情况。
根据之前的说明,可以知道进入的是第一个判断语句块,而其它情况也被我省略了。
再到下面的判断语句,比较乘积之后,就修改width或height,对比重写的方法可以判断,导致效果不同的地方就是这里。代码的逻辑很清晰简单。这里直接取具体值来分析。这里的视频资源的帧宽度是608,帧高度是1080。用来测试的Google Nexus 5是1080×1920。
mVideoWidth * height = 608 × 1920 = 1,167,360,mVideoHeight * width= 1080 × 1080 = 1,166,400,所以修改的是height,等于1,918.4。所以开头说不让底部的导航栏变透明,因为只差两个像素左右,截图看不清。而当底部导航栏不是透明的时候,height是1776。这时候修改的就是width,等于999.8,所以如上面的截图,差别就比较明显了。这么看来,这部分代码就是把VideoView的宽或高给修改了,因为我是指定match_parent的,也就应该是屏幕显示部分的大小。而重写的方法就是跳过了这部分,让VideoView的宽高仍然是match_parent。
用原生VideoView进行全屏播放时的问题的更多相关文章
- audio与video控件/标签的隐藏,iso/Android下自动全屏播放,短暂黑屏问题
(一)audio音频标签 <audio src="xxx.mp3"></audio> (二)video视频标签 <video src="xx ...
- [Winform]Media Player组件全屏播放的设置
摘要 在设置程序开始运行时,让视频全屏播放时,直接设置 windowsMediaPlay.fullScreen = true; 会报错,代码如下 windowsMediaPlay.URL = _vid ...
- Android之针对WebView的全屏播放
转载请标明转载处:http://bbs.csdn.net/topics/390839259 本人刚学android,菜鸟一个,第一次写帖子,最近因为项目要用webview加载html5的视频,开始不能 ...
- 原生JS实现全屏切换以及导航栏滑动隐藏及显示——重构前
思路分析: 向后滚动鼠标滚轮,页面向下全屏切换:向前滚动滚轮,页面向上全屏切换.切换过程为动画效果. 第一屏时,导航栏固定在页面顶部,切换到第二屏时,导航条向左滑动隐藏.切换回第一屏时,导航栏向右滑动 ...
- 【Win 10应用开发】实现全屏播放的方法
有人会问,以前的MediaElement控件不是有现成的一排操作按钮吗?而且可以直接进入全屏播放.是的,我们知道,以往的Store App都是在全屏模式下运行的,只要MediaElement控件填满整 ...
- iphone H5视频行内播放(禁止全屏播放)
一般用户都知道,ios在网页点击视频播放时,视频会弹出全屏播放框. video标签的playsinline.webkit-playsinline标记根本就不会起作用. 还有传闻说对于没有声音的视频不会 ...
- HTML5新标签video在iOS上默认全屏播放
今天做一个app时发现一个问题,应用html5中的video标签加载视频,在Android手机上默认播放大小,但是换成iPhone手机上出问题了,默认弹出全屏播放,查找了好多论坛,都没有谈论这个的.然 ...
- iOS 全局禁止横屏,但UIWebView 全屏播放视频,横屏,解决办法(任意页面横竖屏或禁止)
iOS 全局禁止横屏,但UIWebView 全屏播放视频,横屏,解决办法 时间 2015-07-14 20:59:00 博客园-原创精华区 原文 http://www.cnblogs.com/fe ...
- [转]Android WebView播放视频(包括全屏播放),androidwebview
Android WebView播放视频(包括全屏播放),androidwebview 最近项目开发中用到了WebView播放视频的功能,总结了开发中犯过的错误,这些错误在开发是及容易遇到的,所以我这里 ...
随机推荐
- 一步一步写算法(之n!中末尾零的个数统计)
原文:一步一步写算法(之n!中末尾零的个数统计) [ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 在很多面试的题目中,求n!结果中零的个数也是 ...
- Installshield停止操作系统进程的代码--IS5版本适用
原文:Installshield停止操作系统进程的代码--IS5版本适用 出处:http://www.installsite.org/pages/en/isp_ext.htm这个地址上有不少好东西,有 ...
- MVC5模板部署到mono
VS2013中的MVC5模板部署到mono上的艰辛历程 2014-10-27 09:30 by FuzhePan, 3954 阅读, 46 评论, 收藏, 编辑 部署环境:CentOS7 + Mono ...
- Linux内核头文件与内核与库的关系
看上一篇文章中对buildroot的介绍,里面的文档第 3.1.1.1 Internal toolchain backend 节内容 C库会去访问Linux kernel headers(*.h)文件 ...
- 警惕使用WebClient.DownloadFile(string uri,string filePath)方法
原文:警惕使用WebClient.DownloadFile(string uri,string filePath)方法 WebClient.DownloadFile(string uri,string ...
- leetcode[158] Read N Characters Given Read4 II - Call multiple times
想了好一会才看懂题目意思,应该是: 这里指的可以调用更多次,是指对一个文件多次操作,也就是对于一个case进行多次的readn操作.上一题是只进行一次reandn,所以每次返回的是文件的长度或者是n, ...
- SQL Server 远程链接服务器详细配置
原文:SQL Server 远程链接服务器详细配置[转载] http://hi.baidu.com/luxi0194/blog/item/a5c2a9128a705cc6c2fd7803.html - ...
- c#自带压缩类实现数据库表导出到CSV压缩文件
c#自带压缩类实现数据库表导出到CSV压缩文件的方法 在导出大量CSV数据的时候,常常体积较大,采用C#自带的压缩类,可以方便的实现该功能,并且压缩比例很高,该方法在我的开源工具DataPie中已经经 ...
- Introducing ASP.NET vNext and MVC 6
[译]Introducing ASP.NET vNext and MVC 6 原文:http://www.infoq.com/news/2014/05/ASP.NET-vNext?utm_source ...
- SharePoint 2013常用开发工具
SharePoint 2013常用开发工具分享 2014-04-01 00:59 by jv9, 589 阅读, 1 评论, 收藏, 编辑 众所周知,一款好的开发工具不仅能提高项目开发效率,而且能够协 ...