视频播放的实现大概有以下形式:
1.使用系统自带视频播放类VideoView
2.使用MediaPlayer+surfaceView
3.使用一些第三方框架如:vitamio 还有像新浪在github上开放的视频播放框架等...
使用场景:
第一种方法:简单,但是VideoView不支持自定义视频,也就是你只能使用系统给你提供的布局,这在很大时候是不符合我们项目需求的。
第二种方法:使用MediaPlayer+surfaceView的话支持我们自定义布局,使用起来稍微复杂,这种方法存在的一个缺点就是只是支持MP4 3gp这样的视频编码格式,其他格式的视频均不能播放。
第三种方法:使用第三方框架,不用多说,照着SDK来就行了,相对也简单,同时支持更多的播放格式,再是目前这样的框架好像收费的居多。
SurfaceView在横竖屏切换情况下应如何调整大小
首先先看效果图:
布局如下:
<?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="match_parent"android:layout_height="match_parent"tools:context="com.example.sj.vediodemo.MainActivity"><RelativeLayoutandroid:id="@+id/layout_gesture"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#000"android:gravity="center_horizontal"><SurfaceViewandroid:id="@+id/sv"android:layout_width="match_parent"android:layout_height="match_parent"/></RelativeLayout><Buttonandroid:id="@+id/change"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:onClick="changeOrientation"android:text="横竖屏切换"/>
</RelativeLayout>
很简单,上面一个SurfaceView 下面一个Button 用于横竖屏切换。
在ManiFest.xml 中设置:
<activityandroid:name=".MainActivity1"android:configChanges="keyboard|screenSize|orientation"android:label="@string/app_name"android:screenOrientation="portrait"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity>
设置了一个ScreenOrientation 属性为纵向,同时设置了configChanges 属性,为了防止横竖屏切换导致Activity重新创建。
Main.java 代码:
public class MainActivity1 extends AppCompatActivityimplements SurfaceHolder.Callback, MediaPlayer.OnPreparedListener {public static final float SHOW_SCALE = 16 * 1.0f / 9;private RelativeLayout mSurfaceLayout;private SurfaceView mSurfaceView;private SurfaceHolder mHolder;private MediaPlayer mMediaPlayer;//屏幕宽度private int mScreenWidth;//屏幕高度private int mScreenHeight;//记录现在的播放位置private int mCurrentPos;private boolean isLand;private DisplayMetrics displayMetrics;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main1);displayMetrics = new DisplayMetrics();this.getWindow().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);mScreenWidth = displayMetrics.widthPixels;mScreenHeight = displayMetrics.heightPixels;//后台异常终止,应当恢复原有位置播放if (savedInstanceState != null)mCurrentPos = savedInstanceState.getInt("currentPos", 0);initView();}private void initView() {mSurfaceLayout = (RelativeLayout) findViewById(R.id.layout_gesture);RelativeLayout.LayoutParams lp =(RelativeLayout.LayoutParams) mSurfaceLayout.getLayoutParams();lp.height = (int) (mScreenWidth * SHOW_SCALE);mSurfaceLayout.setLayoutParams(lp);mSurfaceView = (SurfaceView) findViewById(R.id.sv);mHolder = mSurfaceView.getHolder();mHolder.addCallback(this);}private void resetSize() {float areaWH = 0.0f;int height;if (!isLand) {// 竖屏16:9height = (int) (mScreenWidth / SHOW_SCALE);areaWH = SHOW_SCALE;} else {//横屏按照手机屏幕宽高计算比例height = mScreenHeight;areaWH = mScreenWidth / mScreenHeight;}RelativeLayout.LayoutParams layoutParams =new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height);mSurfaceLayout.setLayoutParams(layoutParams);int mediaWidth = mMediaPlayer.getVideoWidth();int mediaHeight = mMediaPlayer.getVideoHeight();float mediaWH = mediaWidth * 1.0f / mediaHeight;RelativeLayout.LayoutParams layoutParamsSV = null;if (areaWH > mediaWH) {//直接放会矮胖int svWidth = (int) (height * mediaWH);layoutParamsSV = new RelativeLayout.LayoutParams(svWidth, height);layoutParamsSV.addRule(RelativeLayout.CENTER_IN_PARENT);mSurfaceView.setLayoutParams(layoutParamsSV);}if (areaWH < mediaWH) {//直接放会瘦高。int svHeight = (int) (mScreenWidth / mediaWH);layoutParamsSV = new RelativeLayout.LayoutParams(mScreenWidth, svHeight);layoutParamsSV.addRule(RelativeLayout.CENTER_IN_PARENT);mSurfaceView.setLayoutParams(layoutParamsSV);}}private void initMediaPlayer() {if (mMediaPlayer != null) {//从Home 返回mMediaPlayer.setDisplay(mHolder);mMediaPlayer.start();} else {mMediaPlayer = new MediaPlayer(); //销毁返回重新初始化try {mMediaPlayer.setDataSource(this, Uri.parse(MediaPath.MEDIA_HENG));mMediaPlayer.setLooping(true);mMediaPlayer.setDisplay(mHolder);mMediaPlayer.setOnPreparedListener(this);mMediaPlayer.prepareAsync();mMediaPlayer.setScreenOnWhilePlaying(true);} catch (IOException e) {e.printStackTrace();}}}//只有在点击Home键或者程序发生异常时才会执行此方法@Overrideprotected void onSaveInstanceState(Bundle outState) {outState.putInt("currentPos", mCurrentPos);super.onSaveInstanceState(outState);}@Overridepublic void surfaceCreated(SurfaceHolder holder) {//SV可见initMediaPlayer();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//SV状态变化}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {if (mMediaPlayer != null) {mMediaPlayer.pause();mCurrentPos = mMediaPlayer.getCurrentPosition();}}/*** 销毁掉MediaPlayer对象*/private void releaseMP() {if (mMediaPlayer != null) {mMediaPlayer.stop();mMediaPlayer.release();mMediaPlayer = null;System.gc();}}@Overridepublic void onPrepared(MediaPlayer mp) {resetSize();if (mCurrentPos != 0) {mMediaPlayer.seekTo(mCurrentPos);}mMediaPlayer.start();}@Overridepublic void onConfigurationChanged(Configuration newConfig) {super.onConfigurationChanged(newConfig);isLand = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);mScreenWidth = displayMetrics.widthPixels;mScreenHeight = displayMetrics.heightPixels;resetSize();}public void changeOrientation(View view) {if (Configuration.ORIENTATION_LANDSCAPE == this.getResources().getConfiguration().orientation) {setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);} else {setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);}}@Overridepublic void onWindowFocusChanged(boolean hasFocus) {super.onWindowFocusChanged(hasFocus);if (hasFocus && Build.VERSION.SDK_INT >= 19) {View decorView = getWindow().getDecorView();decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_FULLSCREEN| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);}}@Overrideprotected void onDestroy() {super.onDestroy();releaseMP();}
}
先来看initView()方法:
1. 目前纵向视频播放一般接受的尺寸为16:9,所以在这我也是设置了16:9的尺寸,视频的宽(RelativeLayout的宽)为手机的宽,视频高的话,根据比例换算。
2.SurfaceView的产生和销毁,都是通过SurfaceHolder进行控制的,添加上SurfaceView的监听事件,这个的话原因想必大家也都知道,一旦Activity切换到后台或者跳转到其他的Activity界面,SurfaceView 就会被销毁,当切换回来Activity界面的时候SurfaceView 又会自动创建,这个时候我们需要重新绑定上新的SurfaceHolder,不然就会出现只是播放声音而不播放画面的问题,代码中每次执行SurfaceCreate()都会执行initMediaPlayer()这个方法,在这个方法中重新绑定了一次SurfaceHoder.
3.播放完毕或者是停止都需要将MediaPlayer资源释放掉。
Ok,主要来 分析一下resetSize()这个方法:
resetSize()这个方法,没别在onPrepare()和onConfigurationChanged中调用到了,一个是视频准备完成,另外一个是横竖屏切换。
53行:
if (!isLand) {// 竖屏16:9height = (int) (mScreenWidth / SHOW_SCALE);areaWH = SHOW_SCALE; } else {//横屏按照手机屏幕宽高计算比例height = mScreenHeight;areaWH = mScreenWidth / mScreenHeight; }
isLand 是否横屏,不是的话根据16:9比例计算出视频框高,是横屏的话,则手机屏幕的高作为视频框的高,比例当然是视频的宽/高,需要注意的是手机横竖屏切换会导致屏幕的宽高颠倒,在onConfigChanged()中已重新获取了手机宽高。
int mediaWidth = mMediaPlayer.getVideoWidth(); int mediaHeight = mMediaPlayer.getVideoHeight();float mediaWH = mediaWidth * 1.0f / mediaHeight;
获取视频大小的宽高比
如果是播放区域宽高比大于视频宽高比的话,这个时候的情况就是视频的高要大一些,那么需要缩放到视频播放框以内,也就是让视频的高等于播放区的高度,if (areaWH > mediaWH) {//直接放会矮胖int svWidth = (int) (height * mediaWH);layoutParamsSV = new RelativeLayout.LayoutParams(svWidth, height);layoutParamsSV.addRule(RelativeLayout.CENTER_IN_PARENT);mSurfaceView.setLayoutParams(layoutParamsSV); }if (areaWH < mediaWH) {//直接放会瘦高。int svHeight = (int) (mScreenWidth / mediaWH);layoutParamsSV = new RelativeLayout.LayoutParams(mScreenWidth, svHeight);layoutParamsSV.addRule(RelativeLayout.CENTER_IN_PARENT);mSurfaceView.setLayoutParams(layoutParamsSV); }
同时按照视频的宽高比进行等比例缩放即可。如果是播放区的宽高比小于视频的宽高比的话,这个时候说明视频的宽高更大一些,我们需要将播放框的宽作为视
频的宽,然后按照等比例缩放计算出视频的高度。计算出宽高之后SurfaceView重新布局即可。SurfaceView 布局在RleativeLayout中设置了居中对其.