视频播放的实现大概有以下形式:
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">
<RelativeLayout
android:id="@+id/layout_gesture"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#000"
android:gravity="center_horizontal">
<SurfaceView
android:id="@+id/sv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
<Button
android: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 中设置:
<activity
android: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 AppCompatActivity
implements 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;
@Override
protected 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:9
height = (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键或者程序发生异常时才会执行此方法
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt("currentPos", mCurrentPos);
super.onSaveInstanceState(outState);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
//SV可见
initMediaPlayer();
}
@Override
public void surfaceChanged(SurfaceHolder holder
, int format, int width, int height) {
//SV状态变化
}
@Override
public 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();
}
}
@Override
public void onPrepared(MediaPlayer mp) {
resetSize();
if (mCurrentPos != 0) {
mMediaPlayer.seekTo(mCurrentPos);
}
mMediaPlayer.start();
}
@Override
public 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);
}
}
@Override
public 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);
}
}
@Override
protected 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:9 height = (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中设置了居中对其.