用原生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的效果。一般情况下,都不会进入判断语句块里。

而这里要关注的其实是最后一句代码,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。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持我们!

(0)

相关推荐

  • 详解Android App中使用VideoView来实现视频播放的方法

    通过VideoView播放视频的步骤: 1.在界面布局文件中定义VideoView组件,或在程序中创建VideoView组件 2.调用VideoView的如下两个方法来加载指定的视频 (1)setVidePath(String path):加载path文件代表的视频 (2)setVideoURI(Uri uri):加载uri所对应的视频 3.调用VideoView的start().stop().psuse()方法来控制视频的播放 VideoView通过与MediaController类结合使用,

  • Android自定义播放器控件VideoView

    介绍 最近要使用播放器做一个简单的视频播放功能,开始学习VideoView,在横竖屏切换的时候碰到了点麻烦,不过在查阅资料后总算是解决了.在写VideoView播放视频时候定义控制的代码全写在Actvity里了,写完一看我靠代码好乱,于是就写了个自定义的播放器控件,支持指定大小,可以横竖屏切换,手动左右滑动快进快退.好了,下面开始. 效果图有点卡,我也不知道为啥..... VideoView介绍 这个是我们实现视频播放最主要的控件,详细的介绍大家百度就去看,这里介绍几个常用的方法. 用于播放视频

  • Android VideoView类实例讲解

    本节使用系统的示例类VideoView继续SurfaceView类相关内容的讲解,以让大家能更深入理解Android系统中图形绘制基础类的实现原理.也许你会发现无法改变VideoView类的控制方面,我们可以通过重构VideoView类来实现更加个性化的播放器.          下面是VideoView类的相关代码. Java 代码 public class VideoView extends SurfaceView implements MediaPlayerControl { privat

  • android使用videoview播放视频

    复制代码 代码如下: public class Activity01 extends Activity{ /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState); setContentView(R.layout.main); final VideoView vid

  • Android使用VideoView播放本地视频和网络视频的方法

    1.效果展示 2.布局文件 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="matc

  • 用原生VideoView进行全屏播放时的问题

    之前参加了一个课程,里面有一节讲到了用视频作为启动界面.讲师用的是自定义VideoView,重写onMeasure方法,因为原生的VideoView在那情况下不能实现全屏播放.当时没有深入研究,现在补回来. 用的是36氪之前的视频(608×1080)和Genymotion中的Google Nexus 5(1080×1920). 一.效果图 1.原生VideoView的效果,这里没有让底部的导航栏也变透明.因为截图上来很难看到差别,后面会解释. xml <?xml version="1.0&

  • Android编程实现WebView全屏播放的方法(附源码)

    本文实例讲述了Android编程实现WebView全屏播放的方法.分享给大家供大家参考,具体如下: 最近因为项目要用webview加载html5的视频,开始不能全屏播,做了很久才做出来!那按我的理解说下怎么实现全屏吧. 首先写布局文件activity_main.xml: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.

  • react-native-video实现视频全屏播放的方法

    react-native-video是github上一个专用于React Native做视频播放的组件.这个组件是React Native上功能最全最好用的视频播放组件,还在持续开发之中,虽然还有些bug,但基本不影响使用,强力推荐. 本篇文章主要介绍下怎么使用react-native-video播放视频,以及如何实现全屏播放,屏幕旋转时视频播放器大小随之调整,显示全屏或收起全屏. 首先来看看react-native-video有哪些功能. 基本功能 控制播放速率 控制音量大小 支持静音功能 支

  • Android编程使WebView支持HTML5 Video全屏播放的解决方法

    本文实例讲述了Android编程使WebView支持HTML5 Video全屏播放的解决方法.分享给大家供大家参考,具体如下: 1)需要在AndroidManifest.xml文件中声明需要使用HardwareAccelerate, 可以细化到Activity级别,如果不需要的View可以声明不要用加速,但是需要在代码中做,具体如下: a. 如果要声明整个应用都要加速: 复制代码 代码如下: <application ... android:hardwareAccelerated ="tr

  • vue-video-player 解决微信自动全屏播放问题(横竖屏导致样式错乱问题)

    对于vue-video-player,从github上找到一段代码,直接放页面!可以了,视频展示出来了!开始下一个功能.... 这可能是大部分前端开发者一贯的思维模式,拿来一个插件,看着demo就做出来了,功能展示正常就OK了,但是一旦出了bug,就会面向百度编程或者面向Google编程! 其实,我也是这样的,哈哈哈哈..... 废话不多说,最近在做一个视频播放的功能,找到vue-video-player插件后,咔咔咔完事,拿着我的爱疯,完美演绎!但是其他人的国产某牌手机,展示的完全不一样,打开

  • Android WebView实现全屏播放视频

    目录 介绍 主要代码 介绍 最近项目开发中用到了WebView播放视频的功能,总结了开发中犯过的错误,这些错误在开发是及容易遇到的,所以我这里总结了一下,希望大家看到后不要再犯类似的错误,尽可能提高开发效率: 这个Demo我这里也参考了网上写的一个比较好的一个Demo,经过总结修改,写出来的. 主要代码 以下是相应代码: MainActivity: package com.androidwebviewdemo; import android.app.Activity; import androi

  • Android如何让WebView中的HTML5页面实现视频全屏播放

    前言 本文主要是将最近工作中遇到的一个问题进行总结分享,主要介绍的是如何让WebView中H5页面全屏播放视频.关于这个问题,做一下简单分析,希望对大家有所帮助,下面话不多说了,来看看详细的介绍吧. 效果图 运行效果 其实很简单,就是配置问题.关键地方配好了,基本没什么问题了. 硬件加速 设置WebView 在清单需要配置的AndroidManifest.xml <application android:allowBackup="true" android:icon="

  • WebView 实现全屏播放视频的示例代码

    最近要支持一个视频挑战的活动,要求 WebView 能全屏播放视频,现在把 Android 端实现的方法分享给大家. 要实现全屏需要给 WebView 设置 WebChromeClient 并覆写 onShowCustomView 和 onHideCustomView() 两方法: webView.setWebChromeClient(new WebChromeClient() { @Override public void onShowCustomView(View view, CustomV

  • Android使用WebView实现全屏切换播放网页视频功能

    首先写布局文件activity_main.xml: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="fill_parent" and

  • WKWebview非全屏自动播放h5视频的实现方法(Swift、OC)

    前言 WKWebview加载一个视频播放的h5页面时,默认是需要用户点击一下才能播放视频,且视频播放时会全屏播放.如果想要h5页面视频非全屏自动播放该怎么做呢? Swift实现 这个时候就需要设置WKWebview的configuration参数,我们先来看一下Swift的实现,代码如下: let configuration = WKWebViewConfiguration() configuration.allowsInlineMediaPlayback = true if #availabl

随机推荐