首先引入IJkplayer依赖:
//ijkPlayer 播放器框架依赖
//required, enough for most devices.足够大部分机型使用
implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8'
// # Other ABIs: optional
// implementation 'tv.danmaku.ijk.media:ijkplayer-armv5:0.8.8'
// implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'
// implementation 'tv.danmaku.ijk.media:ijkplayer-x86:0.8.8'
// implementation 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.8.8'
这里引入armv7a机型的依赖支持就可以啦,足以支持市面上大部分的机型。
IJkplayer GitHub地址:https://github.com/bilibili/ijkplayer
接着编写dialog布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:elevation="@dimen/dp_4"
android:layout_height="280dp">
<com.zc.customview.TextViewIcon
android:id="@+id/delete_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/text_color_one"
android:textSize="@dimen/dp_20"
android:text=""
android:layout_alignParentEnd="true"
android:layout_marginEnd="15dp"
/>
<SurfaceView
android:layout_marginTop="30dp"
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="4dp"/>
<ProgressBar
android:id="@+id/progress"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="6dp"
android:progressDrawable="@drawable/progress_bg"
style="?android:progressBarStyleHorizontal"/>
<android.support.constraint.ConstraintLayout
android:id="@+id/loading_layout"
android:layout_width="@dimen/dp_100"
android:layout_height="@dimen/dp_100"
android:layout_centerInParent="true"
android:visibility="visible"
android:background="@drawable/loading_dialog_layout_bg">
<ImageView
android:id="@+id/progress_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="@id/loading_dialog_title_tv"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/ic_loading_default"
/>
<TextView
android:id="@+id/loading_dialog_title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在加载"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/progress_view"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_gravity="center_horizontal"
android:textSize="@dimen/fifth_text_size"
android:textColor="@color/white"/>
android.support.constraint.ConstraintLayout>
RelativeLayout>
package com.zc.customview;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioManager;
import android.support.constraint.ConstraintLayout;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.ProgressBar;
import com.zc.road.R;
import com.zc.util.LogUtil;
import com.zc.util.RxToast;
import com.zc.util.Util;
import java.io.IOException;
import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import tv.danmaku.ijk.media.player.IMediaPlayer;
import tv.danmaku.ijk.media.player.IjkMediaPlayer;
/**
* 播放视频Dialog
* @author zc
*/
public class VideoDialog implements SurfaceHolder.Callback,IMediaPlayer.OnBufferingUpdateListener,
IMediaPlayer.OnCompletionListener,DialogInterface.OnDismissListener,View.OnClickListener,
IMediaPlayer.OnPreparedListener,IMediaPlayer.OnInfoListener,IMediaPlayer.OnErrorListener{
private SurfaceView surfaceView;
private AlertDialog dialog;
private IjkMediaPlayer mPlayer;
private ProgressBar progressBar;
/**视频路径*/
private String videoUrl;
/**计时服务*/
private ScheduledExecutorService service;
/**加载进店条layout*/
private ConstraintLayout loadingLayout;
/**加载进度条*/
private ImageView loadingView;
/**是否显示加载进度条*/
private boolean isShowLoading;
/**加载进度条旋转动画*/
private RotateAnimation animation;
/**是否第一次加载*/
private boolean isStartLoading;
/**
* 显示dialog
* @param context Context
* @param videoUrl 视频url
*/
public void show(Context context,String videoUrl){
this.videoUrl = videoUrl;
//初始化dialog
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setCancelable(false);
//初始化自定义播放界面
View view = LayoutInflater.from(context).inflate(R.layout.video_dialog_layout, null);
surfaceView = view.findViewById(R.id.surfaceView);
TextViewIcon imageView = view.findViewById(R.id.delete_icon);
progressBar = view.findViewById(R.id.progress);
loadingLayout = view.findViewById(R.id.loading_layout);
loadingView = view.findViewById(R.id.progress_view);
progressBar.setMax(100);
imageView.setOnClickListener(this);
//dialog指定显示内容
builder.setView(view);
dialog = builder.create();
dialog.setOnDismissListener(this);
dialog.show();
Window window = dialog.getWindow();
//设置背景透明
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
WindowManager.LayoutParams lp = window.getAttributes();
lp.gravity = Gravity.CENTER;
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = dp2px(280);
//设置dialog大小
window.setAttributes(lp);
surfaceView.getHolder().addCallback(this);
}
/**
* dp转px
* @param dpValue dp值
* @return Int
*/
private int dp2px(final float dpValue) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
initPlayer();
mPlayer.setDisplay(surfaceView.getHolder());
}
/**
* 初始化Player
*/
private void initPlayer() {
if (mPlayer == null) {
mPlayer = new IjkMediaPlayer();
//设置播放监听
mPlayer.setOnInfoListener(this);
//设置缓冲监听
mPlayer.setOnBufferingUpdateListener(this);
//设置播放完成监听
mPlayer.setOnCompletionListener(this);
// 设置倍速,MediaPlayer是不支持的
mPlayer.setSpeed(1.0f);
//设置加载好视频自动播放
mPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setOnPreparedListener(this);
//设置播放错误监听
mPlayer.setOnErrorListener(this);
try {
if (TextUtils.isEmpty(videoUrl)){
RxToast.warning("视频路径为空");
dialog.dismiss();
return;
}
mPlayer.setDataSource(videoUrl);
} catch (IOException e) {
RxToast.warning("视频路径错误");
dialog.dismiss();
e.printStackTrace();
}
mPlayer.prepareAsync();
isStartLoading = true;
showLoading();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (surfaceView != null) {
surfaceView.getHolder().removeCallback(this);
surfaceView = null;
}
}
/**
* 缓存进度
* @param iMediaPlayer 播放器
* @param i 进度
*/
@Override
public void onBufferingUpdate(IMediaPlayer iMediaPlayer, int i) {
if (progressBar != null) {
progressBar.setSecondaryProgress(i);
//这里Ijkplayer有个缓冲问题 缓冲进度到达95以上就不执行该方法了 所以设置大于98时就缓冲完成
if (i>=98){
progressBar.setSecondaryProgress(100);
}
}
}
/**
* 视频播放完成关闭dialog
* @param iMediaPlayer IMediaPlayer
*/
@Override
public void onCompletion(IMediaPlayer iMediaPlayer) {
if (dialog != null) {
dialog.dismiss();
}
clearTimeService();
}
/**
* 释放关闭Player
*/
private void release() {
if (mPlayer != null) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
}
IjkMediaPlayer.native_profileEnd();
}
/**
* dialog消失回调方法 在这里处理释放player 关闭加载动画
* @param dialog DialogInterface
*/
@Override
public void onDismiss(DialogInterface dialog) {
release();
clearTimeService();
if (animation != null) {
if (isShowLoading){
animation.cancel();
}
animation = null;
}
}
/**
* 点击关闭dialog
* @param v View
*/
@Override
public void onClick(View v) {
if (dialog!=null && dialog.isShowing()){
dialog.dismiss();
}
}
@Override
public void onPrepared(IMediaPlayer iMediaPlayer) {
if (mPlayer != null) {
//开始播放
mPlayer.start();
}
if (service == null) {
//计时服务
service = new ScheduledThreadPoolExecutor(1);
TimerTask task = new TimerTask() {
@Override
public void run() {
if (mPlayer != null) {
//获取百分比显示进度条
int percent = (int) (mPlayer.getCurrentPosition()*100 / mPlayer.getDuration());
progressBar.setProgress(percent);
}
}
};
service.scheduleAtFixedRate(task,10,1000, TimeUnit.MILLISECONDS);
}
}
/**
* 关闭计时服务
*/
private void clearTimeService(){
if (service != null) {
service.shutdownNow();
service = null;
}
}
/**
* 播放监听回调
* @param iMediaPlayer IMediaPlayer
* @param i 当前状态码
* @param i1 之前状态码
* @return boolean
*/
@Override
public boolean onInfo(IMediaPlayer iMediaPlayer, int i, int i1) {
LogUtil.logNormalMsg("VideoDialog", "------------onInfo--------i:"+i);
//当视频由于网速差加载卡顿时返回状态码701(多次测试得知,未查阅官方文档)
if (i==701){
isShowLoading = true;
}else {
isShowLoading = false;
}
isStartLoading = false;
//判断是否显示加载进度条
showLoading();
return false;
}
private void showLoading() {
if (isShowLoading || isStartLoading){
//显示加载进度条
Util.showView(loadingLayout,true);
if (animation == null){
//初始化进度条旋转动画
animation = new RotateAnimation(0,360*74, Animation.RELATIVE_TO_SELF,
0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animation.setDuration(75000);
animation.setRepeatCount(1);
animation.setFillAfter(true);
animation.setInterpolator(new LinearInterpolator());
loadingView.startAnimation(animation);
}
}else {
//隐藏加载进度条 关闭动画
if (animation != null) {
animation.cancel();
animation = null;
}
Util.showView(loadingLayout,false);
}
}
@Override
public boolean onError(IMediaPlayer iMediaPlayer, int i, int i1) {
switch (i){
case -10000:
//视频连接超时
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
RxToast.showTips("视频连接超时,请检查路径是否正确");
break;
default:
//do nothing
break;
}
return false;
}
}
在activity或fragment调用dialog.show()方法播放:
VideoDialog videoDialog = new VideoDialog();
videoDialog.show(this,videoUrl);
最终效果:
注:网速差导致视频卡顿时返回的状态码为701,这个状态码是多次测试得出的结论,并非官方文档中得出的,若有人知道具体状态码还望告知,谢谢!