本文使用Kotlin 编码(提高一下Kotlin的熟悉度 _ !),

使用 MediaPlayer 结合 Lifecycler ViewModle ,完成视频播放的横竖屏切换,状态UI的隐藏

话不多少,上代码

编写 ViewModel ,在 ViewModel 中完成对视频的 【准备工作监听】,【尺寸变化的监听】

PlayerViewModel.kt

class PlayerViewModel:ViewModel(){
    val mediaPlayer = MyMediaPlayerLifecycle() //使用绑定 生命周期的 自定义 MyMediaPlayerLifecycle
   private val _progressBarVisibility = MutableLiveData(View.VISIBLE) 
   val progressBarVisibility : LiveData<Int> = _progressBarVisibility //对加载 progressBar数据进行,保存
   private val _videoResolution = MutableLiveData(Pair(0,0)) //对加载 视频宽高 数据进行,保存
   val videoResolution :LiveData<Pair<Int,Int>> = _videoResolution;
   init{
       loadVideo()
   private fun loadVideo(){
        mediaPlayer.apply{
           _progressBarVisibility.value = View.VISIBLE
            setDataSource("http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f20.mp4")
            setOnPreparedListener{ // 准备监听
                _progressBarVisibility.value = View.INVISIBLE
                isLooping = true //从头开始播放
                it.start()//播放
            setOnVideoSizeChangedListener { mp, width, height -> //尺寸监听
                _videoResolution.value = Pair(width,height)
            prepareAsync()
    override fun onCleared() {
        super.onCleared()
        mediaPlayer.release()
将控件 与 生命周期绑定

MyMediaPlayerLifecycle.kt

// 将控件 与 生命周期绑定
class MyMediaPlayerLifecycle:MediaPlayer(),LifecycleObserver{
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun pausePlayer(){
      pause()
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun resumePlayer(){
        start()
主要方法都在 MainActivity 中

1、surfaceView 有三个 回调方法

  • surfaceChanged() 是肯定要走的回调,可以暂且把方法写在这里面
  • surfaceDestroyed(holder: SurfaceHolder?)
  • surfaceCreated(holder: SurfaceHolder?)

2、横竖屏切换时想要做的事情

onWindowFocusChanged 的 系统回调方法中

override fun onWindowFocusChanged(hasFocus: Boolean) {
    super.onWindowFocusChanged(hasFocus)
    //屏幕 横版的时候隐藏 转态栏
    if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE){
       hideSystemUI()

3、横屏时隐藏一些系统UI (从官网copy的)注释里也都写明了作用

private fun hideSystemUI(){
    //Enables regulapr immersive mode.
    //For "lean back" mode, remove SYSTEM_ _UI_ FLAG_ IMMERSIVE.
    //or for "sticky immersive, replace it with SYSTEM_ UI_ FLAG_ IMMERSIVE_ STICKY
    window.decorView.systemUiVisibility =(View.SYSTEM_UI_FLAG_IMMERSIVE
            //Set the content to appear under the system bars S0 that the
            //Content doesn't resize when the system bars hide and show.
            or View.SYSTEM_UI_LAYOUT_FLAGS
            or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            //Hide the nav bar and status bar
            or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
            or View.SYSTEM_UI_FLAG_FULLSCREEN

4、横竖屏切换时 期望 视频 比例不变

private fun resizePlayer(width: Int, height: Int){
    if (width == 0 || height == 0) return
    surfaceView.layoutParams = FrameLayout.LayoutParams(
        playerFrame.height * width / height, //屏幕高  去适应视频 的宽高比
        FrameLayout.LayoutParams.MATCH_PARENT,
        Gravity.CENTER
MainActivity 完整代码
class MainActivity : AppCompatActivity() {
    private lateinit var playerViewModel: PlayerViewModel;
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        playerViewModel = ViewModelProvider(this).get(PlayerViewModel::class.java)
        playerViewModel.progressBarVisibility.observe(this@MainActivity, Observer {
            progressBar.visibility = it;
        //resize
        playerViewModel.videoResolution.observe(this, Observer {
            playerFrame.post {  //post 是将里面的函数 放在最后执行(等View 都加载完了)
                resizePlayer(it.first,it.second)
        lifecycle.addObserver(playerViewModel.mediaPlayer) //感应生命周期
        surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
            override fun surfaceChanged(
                holder: SurfaceHolder?,
                format: Int,
                width: Int,
                height: Int
                playerViewModel.mediaPlayer.setDisplay(holder)
                playerViewModel.mediaPlayer.setScreenOnWhilePlaying(true) //播放时屏幕点亮
            override fun surfaceDestroyed(holder: SurfaceHolder?) {}
            override fun surfaceCreated(holder: SurfaceHolder?) {}
    private fun resizePlayer(width: Int, height: Int){
        if (width == 0 || height == 0) return
        surfaceView.layoutParams = FrameLayout.LayoutParams(
            playerFrame.height * width / height,
            FrameLayout.LayoutParams.MATCH_PARENT,
            Gravity.CENTER
    override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        //屏幕 横版的时候隐藏 转态栏
        if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE){
            hideSystemUI()
    private fun hideSystemUI(){
        //Enables regulapr immersive mode.
        //For "lean back" mode, remove SYSTEM_ _UI_ FLAG_ IMMERSIVE.
        //or for "sticky immersive, replace it with SYSTEM_ UI_ FLAG_ IMMERSIVE_ STICKY
        window.decorView.systemUiVisibility =(View.SYSTEM_UI_FLAG_IMMERSIVE
                //Set the content to appear under the system bars S0 that the
                //Content doesn't resize when the system bars hide and show.
                or View.SYSTEM_UI_LAYOUT_FLAGS
                or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                //Hide the nav bar and status bar
                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_FULLSCREEN
布局文件:

竖屏的 layout -> activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <FrameLayout
        android:background="#000000"
        android:id="@+id/playerFrame"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="16:9"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
        <SurfaceView
            android:layout_gravity="center"
            android:id="@+id/surfaceView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <ProgressBar
            android:id="@+id/progressBar"
            android:layout_gravity="center"
            style="?android:attr/progressBarStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

横屏的 layout -land -> activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <FrameLayout
        android:id="@+id/playerFrame"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#000000"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
        <SurfaceView
            android:id="@+id/surfaceView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />
        <ProgressBar
            android:id="@+id/progressBar"
            style="?android:attr/progressBarStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />
    </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

到这里就可以进行,简单的视频播放啦!

下一期会加入,进度条,播放/暂停按钮等 触摸控制!~

Android 视频播放器(一) 使用 MediaPlayer本文使用Kotlin 编码(提高一下Kotlin的熟悉度 _!),使用 MediaPlayer 结合 Lifecycler ViewModle,完成视频播放的横竖屏切换,状态UI的隐藏话不多少,上代码编写 ViewModel ,在 ViewModel 中完成对视频的 【准备工作监听】,【尺寸变化的监听】PlayerViewModel.ktclass PlayerViewModel:ViewModel(){ va
一款好用得Android视频播放背景插件视频播放背景插件集成插件添加网络权限配置在部署文件使用控件效果 视频播放背景插件 这款插件即可以使用asses中的视频也可以加载网络上的视频文件,就性能而言,Android上的普通-videoview-成本很高。另外,由于视频分辨率和apk大小,大多数开发人员都不希望在项目中包含视频。有了这个库,开发人员将拥有几行代码的性能友好的视频背景。 1、添加jitpack.io库 root build.gradle中加入代码 maven { url ‘https:/
直接进入主题:本文将会教大家如何实现一个简单的代理服务器(仅支持Http Get),与AndroidMediaPlayer结合,从而可以扩展出“播放 防盗链的媒体文件 ”,“边播放边保存 ”等的功能。 本文的代码可以到这里下载: http://download.... GSYVideoPlayer 一个基于IJkPlayer的播放器 支持调节声音亮度 边播边缓存 使用AndroidVideoCache;ExoPlayer使用SimpleCache 支持多种协议h263\4\5、Https、concat、rtsp、hls、rtmp、crypto、mpeg等等 简单滤镜(马赛克、黑白、色彩过滤、高斯、模糊、模糊等等20多种)、动画、(水印、画面多重播放等) 视频第一帧、视频帧截图功能,视频生成gif功能。 &lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="... 使用Genymotion(或直连手机) anko库 1)封装过了一些SDK方法,使调用更加的方便toast(R.string.message)取代 Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); 2)依赖:implementation "org.jetbrains.anko:anko:0.10.8" 其实,通过阅读上面两个文档,基本上就可以实现自己的需求了,毕竟每个人的需求不一样。由于需求只用到播放音频部分,所以不会设计涉及视频播放。 MediaPlayer位于android.media包下,media下还有一系列相关的API: MediaPlayer :可用于控制音频/视频文件和流的播放。 可以在VideoView找到有关如何使用此类中的方法的示例。 AudioTrack :为应用程序管理和播放单个音频资源。 它允许将 P...