效果图:

1.切换横竖屏

2.重力感应切换横竖屏

3.判断网络状态

videoview布局:

<?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:id="@+id/root_layout"android:layout_width="match_parent"android:layout_height="match_parent"><com.easefun.polyvsdk.ijk.IjkVideoViewandroid:id="@+id/videoView"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_centerInParent="true" /><RelativeLayoutandroid:id="@+id/controllerLayout"android:layout_width="match_parent"android:layout_height="40dp"android:layout_alignParentBottom="true"android:background="#aa000000"><ImageViewandroid:id="@+id/play"android:layout_width="wrap_content"android:layout_height="match_parent"android:paddingLeft="6dp"android:paddingRight="6dp"tools:src="@drawable/icon_play" /><TextViewandroid:id="@+id/currentTime"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_toRightOf="@id/play"android:gravity="center_vertical"android:paddingLeft="5dp"android:paddingRight="5dp"android:textColor="@color/white"android:textSize="12sp"tools:text="00:12" /><SeekBarandroid:id="@+id/seekBar"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginRight="80dp"android:layout_toRightOf="@id/currentTime"android:maxHeight="40dp"android:minHeight="40dp"android:progressDrawable="@drawable/player_setting_bright_progressbar"android:thumb="@drawable/seekbar_button"android:thumbOffset="0dp" /><ImageViewandroid:id="@+id/changeScreen"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_alignParentRight="true"android:paddingLeft="6dp"android:paddingRight="6dp"android:src="@drawable/icon_fullscreen" /><TextViewandroid:id="@+id/totalTime"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_toLeftOf="@id/changeScreen"android:gravity="center_vertical"android:paddingLeft="5dp"android:paddingRight="5dp"android:textColor="@color/white"android:textSize="12sp"tools:text="05:17" /></RelativeLayout>
</RelativeLayout>

XueHuVideoView类
public class XueHuVideoView extends RelativeLayout {@BindView(R.id.videoView)IjkVideoView videoView;@BindView(R.id.play)ImageView play;@BindView(R.id.currentTime)TextView currentTime;@BindView(R.id.seekBar)SeekBar seekBar;@BindView(R.id.changeScreen)ImageView changeScreen;@BindView(R.id.totalTime)TextView totalTime;@BindView(R.id.controllerLayout)RelativeLayout controllerLayout;private ConnectStateReceiver receiver;private Timer timer;private String videoName;private Handler mHandler;private SimpleDateFormat dateFormat = new SimpleDateFormat("mm:ss");private String videoPath;private final int FULL = 0;private final int MIN = 1;private int duration;public int webStatus = -1;public XueHuVideoView(Context context) {super(context);initView(context);}public XueHuVideoView(Context context, AttributeSet attrs) {super(context, attrs);initView(context);}public XueHuVideoView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initView(context);}private void initView(Context context) {View view = LayoutInflater.from(context).inflate(R.layout.xuehu_videoview_layout, null);ButterKnife.bind(this, view);addView(view);}private void setVideoListener() {videoView.setOnPreparedListener(new OnPreparedListener() {@Overridepublic void onPrepared(IMediaPlayer iMediaPlayer) {duration = videoView.getDuration();seekBar.setMax(duration);currentTime.setText("00:00");totalTime.setText(dateFormat.format(new Date(duration)));}});seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {if (fromUser) {videoView.seekTo(progress);}}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {}});videoView.setOnCompletionListener(new IMediaPlayer.OnCompletionListener() {@Overridepublic void onCompletion(IMediaPlayer iMediaPlayer) {if (duration > 0) {EventBus.getDefault().post(new VideoCompleteEvent());videoPause();}}});}public void videoPause() {if (null != timer) {timer.cancel();}videoView.pause();play.setImageResource(R.drawable.icon_play);LoginResponseEntity.Data user = UserData.getUser();if (null != user && !TextUtils.isEmpty(videoName)) {int currentPosition = videoView.getCurrentPosition();UserData.saveVideoPlayRecord(String.valueOf(user.getUserId()), videoName.trim(), currentPosition);}}public void videoStart() {if (!TextUtils.isEmpty(videoName)) {if (null != timer) {timer.cancel();}timer = new Timer();setVideoPlayRecord();videoView.start();play.setImageResource(R.drawable.icon_suspended);timer.schedule(new TimerTask() {@Overridepublic void run() {mHandler.post(new Runnable() {@Overridepublic void run() {seekBar.setProgress(videoView.getCurrentPosition());currentTime.setText(dateFormat.format(new Date(videoView.getCurrentPosition())));}});}}, 0, 1000);}}public void setVideoPath(String url) {setVideoListener();videoPath = url;videoView.setVideoPath(url);videoStart();if (videoPath.startsWith("http")) {registerReceiver();}videoName = url.substring(url.lastIndexOf(File.separator) + 1);}public void registerReceiver() {if (null == receiver) {receiver = new ConnectStateReceiver(this);IntentFilter filter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");getContext().registerReceiver(receiver, filter);}}public void unRegisterReceiver() {if (null != receiver) {getContext().unregisterReceiver(receiver);receiver = null;}}public void setVideoPlayRecord() {LoginResponseEntity.Data user = UserData.getUser();if (null != user && !TextUtils.isEmpty(videoName)) {int record = UserData.getVideoPlayRecord(String.valueOf(user.getUserId()), videoName.trim());if (record > 0) {videoView.seekTo(record);seekBar.setProgress(record);}}}@OnClick({R.id.root_layout, R.id.play, R.id.changeScreen})public void onClick(View view) {switch (view.getId()) {case R.id.root_layout:controllerLayout.setVisibility(controllerLayout.getVisibility() == View.VISIBLE ? View.INVISIBLE : View.VISIBLE);break;case R.id.play:if (videoView.isPlaying()) {videoPause();} else {videoStart();}break;case R.id.changeScreen:if (!TextUtils.isEmpty(videoName)) {int tag = (int) (changeScreen.getTag());switch (tag) {case FULL:VideoPlayFullActivity.start(getContext(), videoPath);break;case MIN:EventBus.getDefault().post(new FullVideoCloseEvent());break;}}break;}}public void setHandler(Handler mHandler) {this.mHandler = mHandler;}public void setChangeScreenMin() {changeScreen.setImageResource(R.drawable.icon_switch_panel);changeScreen.setTag(MIN);}public void setChangeScreenFull() {changeScreen.setImageResource(R.drawable.icon_fullscreen);changeScreen.setTag(FULL);}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();unRegisterReceiver();}
}

调用(拿全屏播放举例):
public class VideoPlayFullActivity extends Activity {@AutowiredString videoPath;@BindView(R.id.videoView)XueHuVideoView videoView;private Handler mHandler = new Handler();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ARouter.getInstance().inject(this);setContentView(R.layout.activity_video_full);ButterKnife.bind(this);EventBusHelp.register(this);videoView.setHandler(mHandler);videoView.setVideoPath(videoPath);videoView.setChangeScreenMin();ScreenRotateUtil.getInstance(this).start(this);videoView.videoStart();}@Overrideprotected void onStart() {super.onStart();videoView.videoStart();}public static void start(Context context, String videoPath) {Intent starter = new Intent(context, VideoPlayFullActivity.class);starter.putExtra(IntentKey.videoPath, videoPath);if (!(context instanceof Activity)) {starter.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}context.startActivity(starter);}@Overrideprotected void onPause() {super.onPause();videoView.videoPause();}@Overrideprotected void onDestroy() {super.onDestroy();if (null != mHandler) {mHandler.removeCallbacksAndMessages(null);mHandler = null;}ScreenRotateUtil.getInstance(this).stop();}@Subscribe(threadMode = ThreadMode.MAIN)public void onEvent(FullVideoCloseEvent event) {finish();}@Subscribe(threadMode = ThreadMode.MAIN)public void onEvent(VideoCompleteEvent event) {finish();}@Subscribe(threadMode = ThreadMode.MAIN)public void onEvent(VideoCloseEvent event) {finish();}
} 

网络状态监听广播

public class ConnectStateReceiver extends BroadcastReceiver {private XueHuVideoView videoView;public ConnectStateReceiver() {super();}public ConnectStateReceiver(XueHuVideoView videoView) {this.videoView = videoView;}@Overridepublic void onReceive(Context context, Intent intent) {ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Service.CONNECTIVITY_SERVICE);NetworkInfo networkInfo = cm.getActiveNetworkInfo();if (null != networkInfo) {int type = networkInfo.getType();if (type != videoView.webStatus) {videoView.webStatus = type;if (type == ConnectivityManager.TYPE_MOBILE) {NetConnectDialog.show(context, videoView);}}}}
}

提示对话框

public class NetConnectDialog {public static void show(Context context, final XueHuVideoView videoView) {videoView.videoPause();AlertDialog alertDialog = new AlertDialog(context).builder();alertDialog.setCancelable(false);alertDialog.setTitle("流量使用提示");alertDialog.setMsg("当前网络无Wi-Fi,继续使用可能会被运营商收取流量费用");alertDialog.setPositiveButton("继续使用", new View.OnClickListener() {@Overridepublic void onClick(View v) {videoView.videoStart();}});alertDialog.setNegativeButton("停止使用", new View.OnClickListener() {@Overridepublic void onClick(View view) {EventBus.getDefault().post(new VideoCloseEvent());}});alertDialog.show();}
}

重力感应

public class ScreenRotateUtil {private static final String TAG = ScreenRotateUtil.class.getSimpleName();private ScreenRotateUtil() {}private static ScreenRotateUtil mInstance;private Activity mActivity;private boolean isClickFullScreen;        // 记录全屏按钮的状态,默认falseprivate boolean isOpenSensor = true;      // 是否打开传输,默认打开private boolean isLandscape = true;      // 默认是竖屏private boolean isChangeOrientation = true;  // 记录点击全屏后屏幕朝向是否改变,默认会自动切换private boolean isEffetSysSetting = false;   // 手机系统的重力感应设置是否生效,默认无效,想要生效改成true就好了private SensorManager sm;private OrientationSensorListener listener;private Sensor sensor;/*** 接收重力感应监听的结果,来改变屏幕朝向*/private Handler mHandler = new Handler(Looper.getMainLooper()) {public void handleMessage(Message msg) {if (msg.what == 888) {int orientation = msg.arg1;if (null == mActivity) {return;}/*** 根据手机屏幕的朝向角度,来设置内容的横竖屏,并且记录状态*/if (orientation > 45 && orientation < 135) {mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);isLandscape = true;} else if (orientation > 135 && orientation < 225) {mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);isLandscape = false;} else if (orientation > 225 && orientation < 315) {mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);isLandscape = true;} else if ((orientation > 315 && orientation < 360) || (orientation > 0 && orientation < 45)) {mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);isLandscape = false;}}}};/*** 初始化,获取实例** @param context* @return*/public static ScreenRotateUtil getInstance(Context context) {if (mInstance == null) {synchronized (ScreenRotateUtil.class) {if (mInstance == null) {mInstance = new ScreenRotateUtil(context);}}}return mInstance;}/*** 初始化重力感应传感器** @param context*/private ScreenRotateUtil(Context context) {// 初始化重力感应器sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);sensor = sm.getDefaultSensor(Sensor.TYPE_GRAVITY);listener = new OrientationSensorListener(mHandler);}/*** 重力感应监听者*/public class OrientationSensorListener implements SensorEventListener {private static final int _DATA_X = 0;private static final int _DATA_Y = 1;private static final int _DATA_Z = 2;public static final int ORIENTATION_UNKNOWN = -1;private Handler rotateHandler;public OrientationSensorListener(Handler handler) {rotateHandler = handler;}public void onAccuracyChanged(Sensor arg0, int arg1) {}public void onSensorChanged(SensorEvent event) {float[] values = event.values;int orientation = ORIENTATION_UNKNOWN;float X = -values[_DATA_X];float Y = -values[_DATA_Y];float Z = -values[_DATA_Z];float magnitude = X * X + Y * Y;// Don't trust the angle if the magnitude is small compared to the y// valueif (magnitude * 4 >= Z * Z) {// 屏幕旋转时float OneEightyOverPi = 57.29577957855f;float angle = (float) Math.atan2(-Y, X) * OneEightyOverPi;orientation = 90 - Math.round(angle);// normalize to 0 - 359 rangewhile (orientation >= 360) {orientation -= 360;}while (orientation < 0) {orientation += 360;}}/*** 获取手机系统的重力感应开关设置,这段代码看需求,不要就删除* screenchange = 1 表示开启,screenchange = 0 表示禁用* 要是禁用了就直接返回*/if (isEffetSysSetting) {try {int isRotate = Settings.System.getInt(mActivity.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION);// 如果用户禁用掉了重力感应就直接returnif (isRotate == 0) return;} catch (Settings.SettingNotFoundException e) {e.printStackTrace();}}// 只有点了按钮时才需要根据当前的状态来更新状态if (isClickFullScreen) {if (isLandscape && screenIsPortrait(orientation)) {           // 之前是横屏,并且当前是竖屏的状态updateState(false, false, true, true);} else if (!isLandscape && screenIsLandscape(orientation)) {  // 之前是竖屏,并且当前是横屏的状态updateState(true, false, true, true);} else if (isLandscape && screenIsLandscape(orientation)) {    // 之前是横屏,现在还是横屏的状态isChangeOrientation = false;} else if (!isLandscape && screenIsPortrait(orientation)) {  // 之前是竖屏,现在还是竖屏的状态isChangeOrientation = false;}}// 判断是否要进行中断信息传递if (!isOpenSensor) {return;}if (rotateHandler != null) {rotateHandler.obtainMessage(888, orientation, 0).sendToTarget();}}}/*** 更新状态** @param isLandscape         横屏* @param isClickFullScreen   全屏点击* @param isOpenSensor        打开传输* @param isChangeOrientation 朝向改变*/private void updateState(boolean isLandscape, boolean isClickFullScreen, boolean isOpenSensor, boolean isChangeOrientation) {this.isLandscape = isLandscape;this.isClickFullScreen = isClickFullScreen;this.isOpenSensor = isOpenSensor;this.isChangeOrientation = isChangeOrientation;}/*** 当前屏幕朝向是否横屏** @param orientation* @return*/private boolean screenIsLandscape(int orientation) {return ((orientation > 45 && orientation <= 135) || (orientation > 225 && orientation <= 315));}/*** 当前屏幕朝向是否竖屏** @param orientation* @return*/private boolean screenIsPortrait(int orientation) {return (((orientation > 315 && orientation <= 360) || (orientation >= 0 && orientation <= 45))|| (orientation > 135 && orientation <= 225));}/*** 根据朝向来改变屏幕朝向** @param isLandscape* @param isNeedChangeOrientation 是否需要改变判断值*/private void changeOrientation(boolean isLandscape, boolean isNeedChangeOrientation) {if (isLandscape) {// 切换成竖屏
            mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);if (isNeedChangeOrientation) this.isLandscape = false;} else {// 切换成横屏
            mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);if (isNeedChangeOrientation) this.isLandscape = true;}}/*** 开启监听* 绑定切换横竖屏Activity的生命周期** @param activity*/public void start(Activity activity) {mActivity = activity;sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);}/*** 注销监听*/public void stop() {sm.unregisterListener(listener);mActivity = null;  // 防止内存泄漏
    }/*** 当前屏幕的朝向,是否是横屏** @return*/public boolean isLandscape() {return this.isLandscape;}/*** 设置系统横竖屏按钮是否生效,默认无效** @param isEffet*/public void setEffetSysSetting(boolean isEffet) {isEffetSysSetting = isEffet;}/*** 旋转的开关,全屏按钮点击时调用*/public void toggleRotate() {/*** 先判断是否已经开启了重力感应,没开启就直接普通的切换横竖屏*/if (isEffetSysSetting) {try {int isRotate = Settings.System.getInt(mActivity.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION);// 如果用户禁用掉了重力感应就直接切换if (isRotate == 0) {changeOrientation(isLandscape, true);return;}} catch (Settings.SettingNotFoundException e) {e.printStackTrace();}}/*** 如果开启了重力i感应就需要修改状态*/isOpenSensor = false;isClickFullScreen = true;if (isChangeOrientation) {changeOrientation(isLandscape, false);} else {isLandscape = !isLandscape;changeOrientation(isLandscape, false);}}}

有几个需要注意的坑

1.ijkVideoView的setVideoPath方法是直接播放的,但是setOnPreparedListener准备是需要时间的,如果4G进入视频页,弹出提示对话框时需要暂停播放,但此时还没有准备好的话,你是没办法暂停的,所以的我用的蠢办法是直接退出整个界面,看你还怎么播

2.全屏切换横竖屏时,清单文件需要配置android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"

3.判断网络状态是,因为广播是一直在的,会一直弹出提示对话框,所以需要标识符来记录之前的网络状态,如果变了且是4G,才会弹框

视频播放器+控制器--封装相关推荐

  1. Android Video Player. 安卓视频播放器,封装 MediaPlayer、ExoPlayer、IjkPlayer。模仿抖音,悬浮播放,广告播放,列表播放,弹幕

    DKVideoPlayer 项目地址:dueeeke/DKVideoPlayer 简介: Android Video Player. 安卓视频播放器,封装 MediaPlayer.ExoPlayer. ...

  2. linux下qt实现vlc视频播放器,Qt封装本地视频播放器(VLC二次开发)

    Qt本地视频播放器 1.使用vlc官方sdk封装,并在QLabel上面播放 2.首先到vlc官网下载vlc的sdk环境,下载地址:http://download.videolan.org/pub/vi ...

  3. Flutter 视频播放器组件封装

    添加插件 pubspec.yaml chewie: ^1.0.0 video_player: ^2.0.2 封装播放器 video_view.dart class VideoView extends ...

  4. 完整视频播放器封装库

    2019独角兽企业重金招聘Python工程师标准>>> 目录介绍 1.关于此视频封装库介绍 1.1 能够满足那些业务需求 1.2 对比同类型的库有哪些优势 2.关于使用方法说明 2. ...

  5. 完整视频播放器封装库,仿优酷

    目录介绍 1.关于此视频封装库介绍 1.1 能够满足那些业务需求 1.2 对比同类型的库有哪些优势 2.关于使用方法说明 2.1 关于gradle引用说明 2.2 添加布局 2.3 最简单的视频播放器 ...

  6. 完整视频播放器封装库 1

    目录介绍 1.关于此视频封装库介绍 1.1 能够满足那些业务需求 1.2 对比同类型的库有哪些优势 2.关于使用方法说明 2.1 关于gradle引用说明 2.2 添加布局 2.3 最简单的视频播放器 ...

  7. 05.视频播放器内核切换封装

    05.视频播放器内核切换封装 目录介绍 01.视频播放器内核封装需求 02.播放器内核架构图 03.如何兼容不同内核播放器 04.看一下ijk的内核实现类 05.看一下exo的内核实现类 06.如何创 ...

  8. 年底了,一起来撸个视频播放器吧!

    /   今日科技快讯   / 近日,有消息传出,百度回港第二上市正式启动,并确定了高盛和中信为其上市保荐团队,并计划春节后正式在港提交上市申请.目前,百度集团对此消息尚无回应. 早在2020年7月下旬 ...

  9. 04.视频播放器通用架构实践

    04.视频播放器通用架构实践 目录介绍 01.视频播放器的痛点 02.业务需求的目标 03.该播放器框架特点 04.播放器内核封装 05.播放器UI层封装 06.如何简单使用 07.如何自定义播放器 ...

  10. android 视频播放器-列表播放器

    github地址 HeartVideo HeartVideo是通过封装Mediaplayer+TextureView的视频播放器,封装此库的初衷是因为开发过程中简单的应用却要加载第三方过大的库增加了包 ...

最新文章

  1. 相较神经网络,大名鼎鼎的傅里叶变换,为何没有一统函数逼近器?答案在这...
  2. leanote个人版安装
  3. idea java 非web程序打包
  4. PB初体验 class one
  5. RxJava 教程第一部分:入门之 关键的类
  6. 温州大学《机器学习》课程课件(六、KNN算法)
  7. Spring Boot——易班优课YOOC课群在线测试自动答题解决方案(六)后端改造
  8. Ubuntu20.04运行帝国时代II征服者
  9. Backbone.js源码解读(转载)
  10. python安装函数库pip网址_批量安装python库函数---pip
  11. Spring,ehcache整合报错
  12. 打开黑色_垃圾桶里的黑色塑料袋,打开一看,倒吸一口气!
  13. SpringBoot2.x(3)---基础入门
  14. android 滑动取值_Android-自定义ViewGroup-上下滑动整体实践下
  15. 以Debug模式启动JBoss
  16. Atlas Resources
  17. 黑苹果 OC引导 big sur 主题分享
  18. 文本表示与文本特征提取的区别
  19. 使用excel2016 制作甘特图
  20. 使用wireshark解密PC浏览器的HTTPS流量

热门文章

  1. SpringBoot2.x填坑(二):elastic search 报错{error:{root_cause:[{type:index_not_found_exception,...
  2. net core文件接收(jpg、png、zip、pdf等自己设置)
  3. C# WinForm的ListView的列排序
  4. 腾讯云服务器连接失败,启动报错:A start job is running for /etc/rc.d/rc.local Compatibility
  5. PHP对银行卡号的几种常见操作
  6. ELK filebeat或logstash修改规则之后重写记录到ElasticSearch
  7. java设计一个user类_java – 如何使用两种不同类型的用户组织OO设计
  8. SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“.
  9. Cause: the class org.apache.tools.ant.taskdefs.optional.ANTLR was not found.
  10. No compiler is provided in this environment. Perhaps you are running on a JRE