Android |
您所在的位置:网站首页 › 平板画中画模式 › Android |
3.1 启动launchMode=“singleTask”的Activity会在画中画窗口中打开 问题 下面这个视频中:我们在主页面(下面简称:MainActivity)中,先打开一个画中画页面(下面简称:PipActivity),再打开一个launchMode=singleTask页面(下面简称:SingleTaskActivity)。奇怪的事情发生了,SingleTaskActivity居然展示在PipActivity中。 官方的demo下也有人提了这个issue,现在还是open状态。 原因分析 我们先通过扔物线老师的这个视频 ,回顾下Activity的四种启动方式、Task、taskAffinity等概念。有了相关的基础知识后,再思考背后的原因。 从上面的奇怪表现看来,我一开始想到的是:PipActivity与MainActivity似乎处于不同的Task。于是尝试打印相关的Task信息。 打开PipActivity后的Task信息。
在上一步的基础上,再打开SingleTaskActivity。
上面的信息解释了SingleTaskActivity为啥会出现在PipActivity窗口中了,因为两者处于同一个Task中。 为啥会出现在同一个Task中呢?常规的standard、singleTop启动时,是直接在当前Task中创建,而singleTask的Activity在启动时会查找taskAffinity相同的Task,刚好这时候应用有两个taskAffinity=11033:com.example.test的Task并且PipActivity所在的Task是最新的,所以启动SingleTaskActivity时就被添加到PipActivity所在的Task了。(以上是个人猜测) 解决 那么如果我们让PipActivity独占一个Task,那么SingleTaskActivity就不会出现在PipActivity窗口中了。 有了大致方向,我首先想到的是:让PipActivity以singleInstance的方式启动。 这样设置后,SingleTaskActivity能正常启动了,但是打开PipActivity时会有转场动画,比较影响体验。 于是换另外一个思路: PipActivity以默认的方式启动,但给它设置一个单独的taskAffinity。 搞定,这样设置后,能满足我们的需求。 设置了单独的taskAffinity会导致任务列表中有两个相同应用的任务,可以设置下面两个参数android:excludeFromRecents="true"和android:autoRemoveFromRecents="true"来保证只有一个。 3.2 无法区分画中画窗口的“最大化”和“关闭”按钮 问题 上图是画中画窗口,红色框框是两个系统自带的按钮:“最大化”按钮和“关闭”按钮。这两个按钮的作用不同,代表的用户目的不一样,需要区分处理。 解决 但可惜系统没有提供区分的方法,不过在stackoverflow上看到一个有意思的解决办法: @Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) { if (!isInPictureInPictureMode) { if (getLifecycle().getCurrentState() == Lifecycle.State.CREATED) { // 用户点击“关闭”按钮 } else if (getLifecycle().getCurrentState() == Lifecycle.State.STARTED){ // 用户点击”最大化“按钮 } } }点击“最大化”按钮或“关闭“按钮时,页面都会退出画中画并回调onPictureInPictureModeChanged方法,但回调时页面的生命周期不一样,可以利用这点来进行判断。自己也验证了一下,上面的代码是没有问题的。 3.3 自定义画中画按钮 画中画窗口最下面这行是自定义按钮区域,我们可以自定义想要的功能。 定义操作按钮 private val mPlayAction get() = RemoteAction( Icon.createWithResource(this, R.drawable.edu_ic_play_video), "", "", PendingIntent.getBroadcast( this, REQUEST_CODE_PLAY, Intent(ACTION_PLAY), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) ) 注意:每个RemoteAction的REQUEST_CODE(比如上面的REQUEST_CODE_PLAY)都应该不一样。 设置操作按钮 可以通过Activity的enterPictureInPictureMode和setPictureInPictureParams进行设置。 监听并响应操作 上面通过PendingIntent.getBroadcast给按钮设置了一个PendingIntent,当按钮被点击时就会触发对应的广播,我们只要监听广播就行了。 // 定义广播接收器 private val mediaActionReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { when (intent?.action) { ACTION_PLAY -> { // 执行播放操作 } } } } // 注册广播接收器 context.registerReceiver(mediaActionReceiver, IntentFilter().apply { addAction(ACTION_PLAY) }) 注意:在不需要的时候,记得通过context.unregisterReceiver取消监听。3.4 开启画中画前的检查 能否开启画中画,受系统版本、画中画权限、系统可运行内存大小等影响,在进入画中画前需要挨个检查。 fun checkOpenPipMode(context: Context): Boolean { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { // Android O以下不支持画中画 return false } val appOpsManager = context.getSystemService(APP_OPS_SERVICE) as AppOpsManager if (AppOpsManager.MODE_ALLOWED != appOpsManager.checkOpNoThrow( AppOpsManager.OPSTR_PICTURE_IN_PICTURE, context.applicationInfo.uid, context.packageName ) ) { // 没有画中画权限 ActivityUtils.startActivity( Intent("android.settings.PICTURE_IN_PICTURE_SETTINGS").apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK } ) return false } if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) { // 低 RAM 设备可能无法使用画中画模式 return false } return true }3.5 开启画中画时,平板的dp发生变化 private fun calculateDp() { val density = application.resources.displayMetrics.density val screenWidth = application.resources.displayMetrics.widthPixels val dp = screenWidth / density println("Test_pip screenWidth=$screenWidth density=$density dp=$dp") }上面这段代码涉及的density和dp是常见屏幕适配方案的关键参数,对页面布局有较大影响。 问题 开发过程中遇到一个情况,平板上打开画中画页面后,此时全局的application.resources.displayMetrics.widthPixels变成以画中画小窗口为依据,导致screenWidth变小,从而导致dp变小,从而导致相关适配的判断出现问题。 上述情况从画中画页面打开,持续到下一个非画中画页面打开,只在部分平板出现,暂时没有找到原因。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |