Picture-In-Picture 画中画模式

此代码为项目中使用,封装了PlayerView,相关进入PIP模式的代码也是在PlayerView中

准备工作

AndroidManifest.xml 中,在对应的Activity下添加:

android:supportsPictureInPicture="true"
android:launchMode="singleTask"
android:configChanges="screenLayout|orientation"

准备进入PIP模式

  1. Android 8.0 Oreo(API Level 26)允许活动启动画中画 Picture-in-picture(PIP)模式,因此开启前需要对当前版本进行判断。
  2. 内存较低的设备可能无法开启PIP,hasSystemFeature(PackageManager. FEATURE_PICTURE_IN_PICTURE) 用来检查以确保可以使用PIP。
 //PlayerView中点击PIP按钮进入PIP模式@Overridepublic void onPipModeClick() {enterPipModel(context);}private PictureInPictureParams.Builder build;public void enterPipModel(Context context) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O&& getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);Activity activity = getActivity(getContext());//Android 10+ 关闭PIP权限后,此处调用checkOp检查会报错if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {if (AppOpsManager.MODE_ALLOWED == appOpsManager.unsafeCheckOpNoThrow(AppOpsManager.OPSTR_PICTURE_IN_PICTURE,context.getApplicationInfo().uid, context.getPackageName())) {buildPIP(activity);} else {showNoPIPPermission(context, activity);}} else {if (AppOpsManager.MODE_ALLOWED == appOpsManager.checkOp(AppOpsManager.OPSTR_PICTURE_IN_PICTURE,context.getApplicationInfo().uid, context.getPackageName())) {buildPIP(activity);} else {showNoPIPPermission(context, activity);}}}}/*** 创建PIP Build** @param activity*/private void buildPIP(Activity activity) {//这里宽高比例写死为16:9,也可用播放器view的宽高build.setAspectRatio(new Rational(16, 9));Rect rect = new Rect();getGlobalVisibleRect(rect);build.setSourceRectHint(rect);//进入PIPactivity.enterPictureInPictureMode(build.build());//对播放器控制view的隐藏playerControlView.forceControlWidgetGone();//回复播放resumePlay();}/*** 提示PIP无权限并且跳转到设置界面** @param context* @param activity*/private void showNoPIPPermission(Context context, Activity activity) {Toast.makeText(context, R.string.player_view_pip_permissions_tips, Toast.LENGTH_LONG).show();try {Intent intent = new Intent("android.settings.PICTURE_IN_PICTURE_SETTINGS", Uri.parse("package:" + activity.getPackageName()));context.startActivity(intent);} catch (Exception e) {e.printStackTrace();}}

PIP模式更新自定义Action

项目需求是暂停恢复播放快进15s快退15s,因此需要在播放器播放、暂停时调用此方法更新PIP模式按钮状态

     //播放视频时,按钮为暂停样式updatePictureInPictureActions(R.drawable.exo_icon_pause,getActivity(getContext()).getString(R.string.cast_pause),PIPControlUtils.CONTROL_TYPE_PAUSE, PIPControlUtils.CONTROL_PAUSE_REQUEST_CODE);//暂停播放时,按钮为播放样式updatePictureInPictureActions(R.drawable.exo_icon_play,getActivity(getContext()).getString(R.string.cast_pause),PIPControlUtils.CONTROL_TYPE_PLAY, PIPControlUtils.CONTROL_PLAY_REQUEST_CODE);
    /*** Update the state of pause/resume/fastforward/rewind action item in Picture-in-Picture mode.** @param iconId      暂停、播放icon id* @param title       action 标题* @param controlType action 类型* @param requestCode PendingIntent 请求码*/void updatePictureInPictureActions(@DrawableRes int iconId, String title, int controlType, int requestCode) {//Android 8.0+if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;//最终PIP模式下自定义按钮的顺序与添加进actions的action顺序相同int flags = PendingIntent.FLAG_UPDATE_CURRENT;if (BuildCompat.isAtLeastS()) {//Android 12 以上需要再添加PendingIntent.FLAG_MUTABLE,否则内部方法checkFlag()会报错flags |= PendingIntent.FLAG_MUTABLE;}final ArrayList<RemoteAction> actions = new ArrayList<>();//暂停、恢复播放的PendingIntentfinal PendingIntent pausePlayIntent =PendingIntent.getBroadcast(getActivity(getContext()),requestCode,new Intent(PIPControlUtils.ACTION_MEDIA_CONTROL).putExtra(PIPControlUtils.EXTRA_CONTROL_TYPE, controlType),flags);//快进的PendingIntentfinal PendingIntent fastForwardIntent =PendingIntent.getBroadcast(getActivity(getContext()),PIPControlUtils.CONTROL_FAST_FORWARD_REQUEST_CODE,new Intent(PIPControlUtils.ACTION_MEDIA_CONTROL).putExtra(PIPControlUtils.EXTRA_CONTROL_TYPE, PIPControlUtils.CONTROL_TYPE_FAST_FORWARD),flags);//快退的PendingIntentfinal PendingIntent rewindIntent =PendingIntent.getBroadcast(getActivity(getContext()),PIPControlUtils.CONTROL_REWIND_REQUEST_CODE,new Intent(PIPControlUtils.ACTION_MEDIA_CONTROL).putExtra(PIPControlUtils.EXTRA_CONTROL_TYPE, PIPControlUtils.CONTROL_TYPE_REWIND),flags);/*** 快退按钮的action*/RemoteAction rewindAction = new RemoteAction(Icon.createWithResource(getActivity(getContext()), R.drawable.ic_rewind_15),"rewind","rewind",rewindIntent);// RemoteAction.setEnabled(boolean); 设置action是否可用,传false则icon变成灰色且不可点击actions.add(rewindAction);/*** 暂停播放按钮的action*/final Icon icon = Icon.createWithResource(getActivity(getContext()), iconId);actions.add(new RemoteAction(icon, title, title, pausePlayIntent));/*** 快进按钮的action*/RemoteAction fastForwardAction = new RemoteAction(Icon.createWithResource(getActivity(getContext()), R.drawable.ic_fast_forward_15),"fast_forward","fast_forward",fastForwardIntent);// setEnabled() -> 可切换action对应的icon是否可用,不可用则为灰色切不能点击actions.add(fastForwardAction);//设置已自定义的actionsbuild.setActions(actions);//设置画中画参数getActivity(getContext()).setPictureInPictureParams(build.build());}

对应的Activity

生命周期:
进入PIP模式:onPause()
从PIP模式回复全屏:onResume()
关闭PIP模式:onStop()
PIP模式下锁屏/解锁:onStop() / onStart()

 //是否进入PIP模式private var isEnteredPIPMode = false//复写finish()方法override fun finish() {finishAndRemoveTask()}override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean,newConfig: Configuration?) {//进入PIP时,会调用onPause(),此时lifecycle.currentState 变为Lifecycle.State.STARTED//退出PIP时,会调用onStop(),此时lifecycle.currentState 变为Lifecycle.State.CREATED,即可做finish Activity的操作//STARTED->:after onStart call;right before onPause call.//CREATED->:after onCreate call;right before onStop call.if (lifecycle.currentState == Lifecycle.State.CREATED) {if (null != mReceiver){unregisterReceiver(mReceiver)mReceiver = null}finish()}super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)isEnteredPIPMode = isInPictureInPictureModeif (isInPictureInPictureMode) {//进入PIP模式后,注册广播接收器以接受自定义ActionregisterBroadcast()}else{if (null != mReceiver){//退出PIP模式后,注销广播接收器,将mReceiver置空unregisterReceiver(mReceiver)mReceiver = null}}}//注册广播接收器private fun registerBroadcast(){mReceiver = object : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {//当前Activity只需要接受来自PIP 自定义Action发出的广播if (PIPControlUtils.ACTION_MEDIA_CONTROL != intent.action) {return}//获取PIP控制类型var controlType = intent.getIntExtra(PIPControlUtils.EXTRA_CONTROL_TYPE,PIPControlUtils.CONTROL_TYPE_PLAY)when (controlType) {//暂停-> 播放PIPControlUtils.CONTROL_TYPE_PLAY -> //播放事件//播放-> 暂停PIPControlUtils.CONTROL_TYPE_PAUSE -> //暂停事件//快进PIPControlUtils.CONTROL_TYPE_FAST_FORWARD -> //快进事件//快退PIPControlUtils.CONTROL_TYPE_REWIND -> //快退事件}}}registerReceiver(mReceiver, IntentFilter(PIPControlUtils.ACTION_MEDIA_CONTROL))}override fun onStop() {super.onStop()if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {/*** isInPictureInPictureMode -> false,画中画模式时点了关闭按钮调用的onStop* 注意:Android 12版本退出PIP模式时isInPictureInPictureMode未变成false* 因此在onPictureInPictureModeChanged()方法里去判断是不是退出PIP模式*///isInPictureInPictureMode -> true,画中画模式时息屏调用的onStopif (isInPictureInPictureMode) {//此处可以处理进入PIP模式后息屏下是否需要暂停}}}

PIPControlUtils

class PIPControlUtils {companion object {//PendingIntent actionconst val ACTION_MEDIA_CONTROL = "media_control"//PendingIntent extra nameconst val EXTRA_CONTROL_TYPE = "control_type"//PendingIntent extra resume playconst val CONTROL_TYPE_PLAY = 0x800//PendingIntent extra pauseconst val CONTROL_TYPE_PAUSE = 0x801//PendingIntent extra fast forwardconst val CONTROL_TYPE_FAST_FORWARD= 0x802//PendingIntent extra rewindconst val CONTROL_TYPE_REWIND = 0x803//Fast forward time millisconst val FAST_FORWARD_TIME = 15 * 1000//Rewind time millisconst val REWIND_TIME = 15 * 1000//PendingIntent play request codeconst val CONTROL_PLAY_REQUEST_CODE = 0x800//PendingIntent pause request codeconst val CONTROL_PAUSE_REQUEST_CODE = 0x801//PendingIntent fast forward request codeconst val CONTROL_FAST_FORWARD_REQUEST_CODE = 0x802//PendingIntent rewind request codeconst val CONTROL_REWIND_REQUEST_CODE = 0x803}
}

Picture-In-Picture 画中画模式相关推荐

  1. iOS画中画Picture in Picture:你需要知道的9个知识点

    iOS9最后给我们带来了每个人期待已久的"多任务",Slider Over.Split View 和画中画(PiP),已经使iPad成为一个比之前更强大.更便利的工具.使你在工作中 ...

  2. iOS9 画中画 Picture in Picture

    画中画 (Picture in Picture) iOS9系统在iPad上支持多任务分屏和画中画视频播放,画中画视频播放就将视频播放窗口化,然后浮动在屏幕上,此时你可以使用其他APP.但是有限制:1. ...

  3. Android开发笔记(一百六十七)Android8.0的画中画模式

    前面的博文< Android开发笔记(一百五十九)Android7.0的分屏模式>介绍了Android7.0的多窗口特性,但是这个分屏的区域是固定的,要么在屏幕的上半部分,要么在屏幕的下半 ...

  4. 总结系列-Android画中画模式-看这篇就够啦

    最近做做播放器,有个浮窗播放的需求,两种实现方式,一种是申请浮窗权限,创建浮窗参考 flowWindow,一种是采用画中画模式(8.0以上) 关于画中画 Android 8.0 Oreo(API Le ...

  5. 如何使用Google Chrome的画中画模式

    Chrome's picture-in-picture mode that allows you to float videos on top of other windows is live in ...

  6. 微软新版edge浏览器如何开启画中画模式 (微软新版edge)

    方法一 打开edge,地址栏输入edge://flags并回车,搜索标签Global,然后将全局媒体控制和画中画设置为Enabled,最后重启即可 方法二 添加扩展搜索Picture in Pictu ...

  7. [UWP]用画中画模式(CompactOverlay Mode)让用总在最前端显示

    1. 什么是,以及怎么用画中画 Windows 10 Creators Update以后UWP提供了一个新的视图模式CompactOverlay,中文翻译成 紧凑的覆盖层?反正大部分时间我们都会称它为 ...

  8. android 画中画模式自定义,Android 8.0 Oreo 画中画模式

    Android 8.0 Oreo(API Level 26)允许活动启动画中画 Picture-in-picture(PIP)模式.PIP 是一种特殊类型的多窗口模式,主要用于视频播放.PIP 模式已 ...

  9. Android 8.0 学习(21)---Oreo的画中画模式学习

    Android 8.0 Oreo的画中画模式学习 本文主要是对谷歌开发者官方微信公众号发布的Android 8.0 Oreo 画中画模式一文的学习记录.  画中画模式Picture-in-pictur ...

  10. Android实现一键开启自由窗口、分屏、画中画模式——画中画模式

    转载请注明出处:https://blog.csdn.net/sunmmer123 Android实现一键开启自由窗口.分屏.画中画模式系列 一键开启进入自由窗口模式 一键开启进入分屏模式 一键进入画中 ...

最新文章

  1. C语言字符串处理的库函数
  2. git 提交的时候报错:error: 'flutter_app/' does not have a commit checked out
  3. 2018年视觉所有干货博文的分类汇总
  4. MySQL查询表内重复记录
  5. Markdown 语法及常用资料收集--CheatSheet
  6. 框架:AspectJ
  7. SQ小组KTV点歌系统简介
  8. java 定时任务(三):cron表达式
  9. [css] 用CSS绘制一个红色的爱心
  10. TensorFlow 简介
  11. 数字信号处理——DFT的一些理解
  12. 51CTO大赛,欢迎投博主一票
  13. 电子健康行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
  14. Java权限管理系统之数据库设计(一)
  15. 分类与聚类的本质区别
  16. 试验设计第二版茆诗松课后题答案_试验设计习题及答案
  17. 怎么查看当前系统jdk版本
  18. 副高 职称计算机 上海,高级职称评定
  19. [系统控件重绘教程(一)]重绘NSWindow
  20. 为什么这么多人怼我?或许是这个原因

热门文章

  1. 计算机的CPU和GPU的区别,CPU 和 GPU 有什么区别
  2. 尖叫吧!2015创新中国春季峰会 880元VIP门票免费送
  3. 唐骏的八大“职业经理潜规则”辨析
  4. 智能车载终端项目方案
  5. Turf.js——用于地理空间分析的js库,处理各种地图算法
  6. 游戏自评——英雄无敌手游
  7. 第16章 第一个信徒
  8. 代驾行业开发APP需要注意哪些
  9. 附彩蛋|Spring Security 竟然故意延长登录时间?知道真相的我惊呆了!
  10. 企业变革与创新 | 如何打造创新”永动机“?