文章目录

  • 前言
  • 一、先来张效果图
  • 二、使用步骤
    • 1.配置清单文件
    • 2.编写 Service
    • 3. Activity
    • 4.请求权限
    • 5.浮窗的页面贴一下
  • 三、画中画
  • 总结

前言

本篇以简单的浮窗视频为例, 练习 Service, 浮窗, MediaPlayer视频播放等;

本篇涉及内容:

  • Service 的基本用法;
  • MediaPlayer 播放本地视频
  • 通过 WindowManager 添加浮窗
  • Android Result API 自定义协议类, 校验浮窗权限

一、先来张效果图

二、使用步骤

1.配置清单文件

<!-- SD卡读写权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><!-- 8.0 以上 前台服务权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /><!-- 悬浮窗权限 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /><application...<!-- 读取本地文件需要 -->android:requestLegacyExternalStorage="true" ><!-- 自定义的视频浮窗 Service --><service android:name=".test.textservice.VideoFloatingService"/>

2.编写 Service

这次的 Service 并没有 上一篇 简单的音乐播放器 中的 Service 复杂;
只需要初始化 MediaPlayer, 播放视频. 添加悬浮窗. 以及内部简单的控制逻辑;

class VideoFloatingService: Service() {private val TAG = "VideoFloatingService"private lateinit var windowManager: WindowManagerprivate lateinit var layoutParams: WindowManager.LayoutParamsprivate lateinit var binding: ViewVideoFloatingBindingprivate lateinit var mediaPlayer: MediaPlayeroverride fun onCreate() {super.onCreate()init()  // 初始化播放器showFloatingWindow()    // 添加浮窗}override fun onBind(intent: Intent?): IBinder? = nullprivate fun showFloatingWindow() {windowManager = getSystemService(WINDOW_SERVICE) as WindowManager// 初始化浮窗 layoutParamslayoutParams = WindowManager.LayoutParams().also {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {it.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY} else {it.type = WindowManager.LayoutParams.TYPE_PHONE}it.format = PixelFormat.RGBA_8888it.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL orWindowManager.LayoutParams.FLAG_NOT_FOCUSABLE// 浮窗位置和尺寸, 偏移量等it.gravity = Gravity.CENTER_VERTICAL or Gravity.STARTit.width = WindowManager.LayoutParams.WRAP_CONTENTit.height = WindowManager.LayoutParams.WRAP_CONTENTit.x = 0it.y = 0}// 初始化浮窗布局, 为播放按钮添加事件; 为整个浮窗添加触摸监听(满足拖动, 点击等)binding = ViewVideoFloatingBinding.inflate(LayoutInflater.from(this))binding.ivPlay.setOnClickListener {binding.ivPlay.setImageResource(if(mediaPlayer.isPlaying){mediaPlayer.pause()R.mipmap.video_icon_play}else{mediaPlayer.start()R.mipmap.video_icon_suspend})}// 添加拖拽事件binding.root.setOnTouchListener(FloatingListener())// 要等 SurfaceHolder Createdbinding.svVideo.holder.addCallback(object : SurfaceHolder.Callback {override fun surfaceCreated(holder: SurfaceHolder) {mediaPlayer.setDisplay(binding.svVideo.holder)}override fun surfaceChanged(s: SurfaceHolder, f: Int, w: Int, h: Int ) {}override fun surfaceDestroyed(holder: SurfaceHolder) {}})// 添加浮窗windowManager.addView(binding.root, layoutParams)}private fun init() {mediaPlayer = MediaPlayer().also {it.isLooping = true // 循环播放val file = File(Environment.getExternalStorageDirectory(),"big_buck_bunny.mp4")it.setDataSource(file.path)it.prepareAsync()it.setOnPreparedListener {mediaPlayer.start()}}}override fun onDestroy() {mediaPlayer.stop()mediaPlayer.release()windowManager.removeView(binding.root)super.onDestroy()}inner class FloatingListener: View.OnTouchListener{private var x = 0   // 当前位置值private var y = 0private var cx = 0  // 点击初始值;private var cy = 0private var checkClick: Boolean = false@SuppressLint("ClickableViewAccessibility")override fun onTouch(v: View?, event: MotionEvent?): Boolean {when(event?.action){MotionEvent.ACTION_DOWN -> {checkClick = truecx = event.rawX.toInt()cy = event.rawY.toInt()x = cxy = cy}MotionEvent.ACTION_MOVE -> {val nowX = event.rawX.toInt()val nowY = event.rawY.toInt()val movedX = nowX - xval movedY = nowY - yx = nowXy = nowYlayoutParams.let {it.x = it.x + movedXit.y = it.y + movedY}// 更新悬浮窗控件布局windowManager.updateViewLayout(binding.root, layoutParams)if (checkClick && (abs(x - cx) >= 5 || abs(y - cy) >= 5)) {checkClick = false}}MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {if (checkClick && abs(cx - event.rawX.toInt()) < 5 && abs(cy - event.rawY.toInt()) < 5) {// 判断为 浮窗点击事件;  控制播放按钮显隐;if(binding.ivPlay.visibility == View.VISIBLE){binding.ivPlay.visibility = View.GONE}else{binding.ivPlay.visibility = View.VISIBLEbinding.ivPlay.setImageResource(if(mediaPlayer.isPlaying){R.mipmap.video_icon_suspend} else {R.mipmap.video_icon_play})}}checkClick = false}}return true}}
}

代码看起来不少, 但逻辑却很简单; 总体来看只有 初始化播放器, 添加浮窗, 添加触摸监听 等;

3. Activity

Activity 就很简单了, 先校验或请求一下权限. 然后直接启动 Service 就完事了;

// 点击事件
fun onClick(v: View) {when(v.id){R.id.tv_one -> {startFloatingWindow()   // 检验权限后 开启Service}R.id.tv_two -> {stopService(Intent(this, VideoFloatingService::class.java))}}
}private fun startFloatingWindow() {// Android Result Api 的方式, 请求权限launcher2.launch(null)
}private val launcher2 = registerForActivityResult(RequestFloating(this)) {if(it){// toast("") 这是 kotlin 自定义的扩展函数. toast("有权限")    // 有了权限, 直接启动 Service 即可startService(Intent(this, VideoFloatingService::class.java))}else{toast("授权失败")}
}

4.请求权限

这是 俺自定义的 协议类; 有权限时 同步响应回调; 没有权限, 则跳转开启权限的页面;

class RequestFloating(val context: Context): ActivityResultContract<Unit, Boolean>(){@RequiresApi(Build.VERSION_CODES.M)override fun createIntent(context: Context, input: Unit?) = Intent().also {it.action = Settings.ACTION_MANAGE_OVERLAY_PERMISSIONit.data = Uri.parse("package:${context.packageName}")}override fun getSynchronousResult(context: Context, input: Unit?): SynchronousResult<Boolean>? {return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(context)) {null} else {SynchronousResult(true)}}override fun parseResult(resultCode: Int, intent: Intent?): Boolean{return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context)}
}

不了解 Android Result Api 的小伙伴可以看我这篇文章: Android Result API

当然也可以用下面这段代码:

//检查悬浮窗权限,小于6.0系统不需要权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);intent.setData(Uri.parse("package:" + getPackageName()));startActivityForResult(intent, 100);}else{//已有权限}

这种. 还得重写 onActivityResult; 但是 onActivityResult 中并没有结果, 需要自己再判断一遍

5.浮窗的页面贴一下

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"><SurfaceViewandroid:id="@+id/sv_video"android:layout_width="180dp"android:layout_height="108dp"app:layout_constraintTop_toTopOf="parent"app:layout_constraintStart_toStartOf="parent"/><ImageViewandroid:id="@+id/iv_play"style="@style/img_wrap"android:layout_width="30dp"android:layout_height="30dp"android:src="@mipmap/video_icon_play"android:visibility="gone"app:layout_constraintTop_toTopOf="parent"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintEnd_toEndOf="parent"/></androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Activity 的页面就不贴了; 就俩按钮.
至此, 简单的视频浮窗播放 Service 完成

Service: 三、小窗口(浮窗) 播放视频相关推荐

  1. Android悬浮窗播放视频

    大家应该很喜欢这样的场景:一边打游戏一边看视频,生活娱乐两不误.这样应该怎么去实现呢?Android有提供悬浮窗API,使用悬浮窗播放视频,可以悬浮在其他应用上.有人可能会说,悬浮窗是不是会遮挡界面, ...

  2. android浮窗播放器,Android仿优酷视频的悬浮窗播放效果

    之前接了需求要让视频播放时可以像优酷视频那样在悬浮窗里播放,并且悬浮窗和主播放页面之间要实现无缝切换,项目中使用的是自封装的ijkplayer 这个要求就代表不能在悬浮窗中新建视频控件,所以需要在悬浮 ...

  3. 网页视频小窗口置顶播放

    小窗口分离 Separate-Window_v0.7.6 用法: 最新chrome浏览器需要解压将文件夹添加到扩展中 安装完毕,点击Select,选中视频即可分离出小窗口. 若黑屏无法播放,鼠标滑轮滑 ...

  4. Android音乐浮窗播放器

    这是来到公司的第一个小项目.做一个音乐播放器,内容如下 Float Music(浮窗音乐播放器) 考点: 服务, 音乐播放, 浮窗界面, 列表界面, 自定义view, 手势操作 功能描述: 1.音乐列 ...

  5. 微信小程序使用video播放视频失败

    解决方案: src="xxx"使用视频的链接地址而不是网页地址,右键选择"复制视频链接"试试

  6. firefox视屏小窗口置顶播放

  7. Android小窗口模式,picture-in-picture(PIP画中画)的使用

    1.介绍 Android8.0的时候推出了画中画模式,可以让Activity缩小显示在其他Activity上方.当初我维护的项目本身自己实现了这个功能,Android加入画中画之后两个功能并行,互相交 ...

  8. android qq视频悬浮窗口,手机QQ浏览器浮窗视频实现边看视频边操作

    我们在使用手机浏览器的时候,可能都会有这样的体会.那就是当您需要开启一个在线视频观看时,就很难以去实现如同PC电脑平台那样,边看视频边在浏览器上进行其他的冲浪操作.这个功能其实所涉及到的,如果在PC平 ...

  9. Android视频播放器实现小窗口和全屏状态切换

    Android视频播放器实现小窗口和全屏状态切换 实在是不好意思,楼下评论的兄弟久等了,这文章一直没写第一是没时间,第二是自己准备也不充足,最近才看了好几个Android视频播放器的开源项目,才对视频 ...

最新文章

  1. 【二分答案】【最短路】bzoj1614 [Usaco2007 Jan]Telephone Lines架设电话线
  2. Apache Commons IO教程:初学者指南
  3. zoho配置dmarc_停止[营销]电子邮件反弹! 如何配置SPF,DMARC和DKIM
  4. 对waitpid 的学习
  5. Windwos8.1下配置PHP环境
  6. ThinkPad SL400使用手札
  7. 模拟将本地文件上传至外服务器
  8. 新南威语言班C加,【干货来了】新南威尔士大学UEEC语言班 你了解多少?
  9. 两种方法:在 PowerPoint 中插入视频
  10. 移动端安卓开发学习记录--Android Studio使用adb链接夜神模拟器常用指令
  11. linux可用直播软件,免费直播软件OBS Studio下载 支持Windows/Mac和Linux
  12. Spring 之AOP AspectJ切入点语法详解(最全了,不需要再去其他地找了)---zhangkaitao
  13. iOS UITextView 纯英文排版换行出错的解决方案
  14. 如何使用css实现三角形?
  15. Android开发框架汇总
  16. MySQL之concat的用法
  17. Beta Round #43 (ACM-ICPC Rules), problem: (E) Comb 优先队列+晦涩题意
  18. 电脑 屏幕尺子 量角器
  19. 360桌面助手计算机打不开怎么办,桌面助手360【处理指南】
  20. 计算机的系统安装步骤,计算机系统安装步骤是什么

热门文章

  1. javaScript实现抽奖大转盘(一)
  2. MOS管开关速度相关参数
  3. 判断当前时间为本月的第几周,本周的第几天
  4. Multipart/form-data文件上传简介
  5. Baizhi Memcached GJF
  6. 通俗解释:什么是临床预测模型
  7. JS中attr和prop区别
  8. win10无限重启_win10系统重置教程
  9. 云计算技术与应用 - 知识点
  10. okhttp3调用接口超时