Android 画中画避坑指北

一、背景

之前项目中遇到了一个需求,需要使用画中画功能,原本以为将是一个比较小的功能点,但是真正实际走下来发现这样一个小功能实际上有很多坑点,所以总结一下。

二、避坑指北

1、声明支持画中画

在 AndroidManifest.xml 中声明 Activity 支持 PiP:

xml 复制代码
<activity android:name=".YourActivity"
    android:supportsPictureInPicture="true" />

2、Android画中画仅支持8以上的机型

小于安卓8的手机就需要隐藏画中画的按钮

3、进入画中画模式:enterPictureInPictureMode

enterPictureInPictureMode 是 Android 中用于将当前 Activity 切换到画中画(PiP)模式的方法。

参数: params: PictureInPictureParams 对象,包含画中画窗口的配置参数

其中我们可以在PictureInPictureParams对象中设置按钮图标。

4、是否是画中画模式:isInPictureInPictureMode()

调用 Activity#isInPictureInPictureMode()可以判断当前是否为画中画模式。

5、画中画状态切换监听

java 复制代码
@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
}

重写 onPictureInPictureModeChanged 用于监听应用进入或退出画中画(Picture-in-Picture, PiP)模式的状态变化。

isInPictureInPictureMode

  • true 表示当前进入 PiP 模式
  • false 表示退出 PiP 模式

6、画中画权限问题

画中画的功能权限默认都是赋予的,但是也可以通过画中画里面设置按钮,去把这个权限给关闭,这个时候就需要判断此时是否有这个权限,否则就无法开启。

判断权限是否开启的方法:

java 复制代码
public static boolean hasPipPermission(){
    AppOpsManager appOpsManager = (AppOpsManager) App.getContext().getSystemService(Context.APP_OPS_SERVICE);
    return appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_PICTURE_IN_PICTURE, android.os.Process.myUid(), App.getContext().getPackageName()) == AppOpsManager.MODE_ALLOWED;
}

跳转到系统画中画权限设置页面的代码:

java 复制代码
activity.startActivity(new Intent("android.settings.PICTURE_IN_PICTURE_SETTINGS", Uri.parse("package:" + activity.getPackageName())));

这里需要注意,必须要使用activity来启动Activity,如果用Application Context就无法跳转

7、画中画模式下,功能按钮的设置

第一种可以使用MediaSessionCompat来进行设置,不过这种方式无法替换icon,局限性比较大。就不再介绍了。

第二种可以使用setActions进行设置,也是官方推荐的方式。

这里就有几个细分的问题:

icon的点击事件如何处理

创建Action会传入一个PendingIntent,然后点击之后会发送广播,在广播接收器中添加事件处理。

java 复制代码
private RemoteAction createRemoteAction(int iconResId, String action) {
    return new RemoteAction(
            Icon.createWithResource(this, iconResId),
            "",
            "",
            PendingIntent.getBroadcast(
                    this,
                    0,
                    new Intent(action),
                    PendingIntent.FLAG_IMMUTABLE
            )
    );
}

广播接收器

java 复制代码
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent == null) {
            return;
        }
        LogWriter.writeLog(TAG, "BroadcastReceiver onReceive() action:" + intent.getAction());
        if (ACTION_START.equals(intent.getAction())) {
            //
        } else if (ACTION_PAUSE.equals(intent.getAction())) {
            //
        } else if (ACTION_PRE.equals(intent.getAction())) {
            //
        } else if (ACTION_NEXT.equals(intent.getAction())) {
            //
        }
    }
};

这里还需要注意不同的action都需要注册,否则无法识别这些按钮

java 复制代码
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_START);
intentFilter.addAction(ACTION_PAUSE);
intentFilter.addAction(ACTION_PRE);
intentFilter.addAction(ACTION_NEXT);
registerReceiver(mBroadcastReceiver, intentFilter);

icon如何动态切换

在需要切换icon的时候,需要调用,来重新刷新配置,来替换icon

scss 复制代码
setPictureInPictureParams(pictureInPictureParams);

广播接收器的问题

因为一般要在广播接收器中去调用setPictureInPictureParams来切换icon。所以必须要注意,需要在Activity的onDestroy()中去反注册广播接收器。 前面的页面内关闭,在新页面也会收到之前页面的广播,在调用setPictureInPictureParams方法时,会爆出token异常的问题。而且也会造成内存泄漏。

8、vivo s7手机打开画中画模式会延迟3500ms左右

如果theme设置了windowIsTranslucent,就会出现这个问题

xml 复制代码
<item name="android:windowIsTranslucent">true</item>

解决办法:就是把这个参数去掉。

9、部分机型会出现进入画中画时会有白色闪一下的问题(比如折叠屏手机)

解决办法:

在启动画中画时,调用setSourceRectHint()方法,配置视频源的位置。视频源位置,如果直接能获取到,那就直接拿来用。一般情况下只能通过屏幕的位置,以及视频的宽高来进行计算,下面就是计算的方法。

java 复制代码
private Rect getSourceRect(Rational rational){
    Rect screenRect = new Rect();
    getWebview().getGlobalVisibleRect(screenRect);
    float screenRation = (float)screenRect.width() / (float)screenRect.height();
    float videoRation = rational.floatValue();
    if(videoRation >= screenRation){
        int height = (int) (screenRect.width() /  videoRation);
        int top = (screenRect.height() - height) / 2;
        int bottom = top + height;
        Rect launchBounds = new Rect(0, top, screenRect.right, bottom);
        return launchBounds;
    }else{
        int width = (int) (videoRation * screenRect.height());
        int top = 0;
        int left = (screenRect.width() - width) / 2;
        int right = left + width;
        int bottom = screenRect.bottom;
        Rect launchBounds = new Rect(left, top, right, bottom);
        return launchBounds;
    }
}

10、画中画启动后,其他页面启动,可能会在画中画内部打开

如果画中画以默认形式打开,背后的页面以无activity的模式来打开页面,那么新的页面就会在画中画中被打开。

解决方案:

方案一,背后的页面不能以无activity的模式来打开页面,必须传入activity来打开。这样改动量非常大,而且有些地方只能以无activity的模式来打开页面。这个方案局限性就很大。

方案二,将画中画的Activity设置为新的task

xml 复制代码
android:taskAffinity=".webForVideo" 
android:excludeFromRecents="true"

一般来说,都会搭配excludeFromRecents=true来使用,防止在后台任务页中,出现两个。

11、画中画宽高比的问题

指定宽高比

如果进入画中画模式不指定宽高比的话,就会按照默认的大小来打开画中画,那么竖屏的视频也会按照横屏的形式来播放。可以调用来指定宽高比。

java 复制代码
setAspectRatio()

宽高比限制问题

根据系统提示,画中画宽高比是有大小限制的,最大是239:100 最小是100:239,如果设置的宽高比超过了这个限制,那么就会出现报错。

所以就需要添加限制,超过最大值,就以最大值进行展示。

Java 复制代码
Rational aspectRatio = new Rational(mVideoUrlBean.getWidth(), mVideoUrlBean.getHeight());
//限定最大值与最小值 1:2.39 2.39:1 否则会crash
Rational maxAspectRatio = new Rational(239, 100);
Rational minAspectRatio = new Rational(100, 239);
if (aspectRatio.floatValue() > maxAspectRatio.floatValue()) {
    aspectRatio = maxAspectRatio;
} else if (aspectRatio.floatValue() < minAspectRatio.floatValue()) {
    aspectRatio = minAspectRatio;
}
builder.setAspectRatio(aspectRatio);

12、直接进入画中画不够丝滑

如果使用ARoute的方式来启动需要画中画的页面,那么在页面跳转、关闭的时候,会有动画。所以一般的情况下,这些动画都不需要,所以可以采用默认的startActivity(new Intent())的方式来启动。

13、画中画,大小切换时,大部分机型会出现闪烁

大概原因定位到是,大小切换时,会走onConfigChange(),在onConfigChange时,WebView会重新布局,从而导致了闪烁,但是不好修复。查看了官方的demo也有这个问题,所以还需要再看看有没有其他办法解决。

14、直接以画中画模式打开,会先出现黑影

目前直接以画中画模式打开,是先打开主体页面,然后再开启画中画。因为主体页面是黑色的,所有会出现黑影闪过。看后面是否可以先将主体页面设置为透明色。不过目前页面设置为透明色还有很多问题。官方demo直接以画中画形式打开也会存在黑影的情况。

三、总结

以上大致总结了一下,在开发以及测试过程中遇到的一些坑点,虽然比较细碎,但是都是实际的项目经验,希望能够读者带来一些思考。

相关推荐
IT从业者张某某14 分钟前
信奥赛-刷题笔记-队列篇-T3-P3662Why Did the Cow Cross the Road II S
android·笔记
未来之窗软件服务32 分钟前
Cacti 未经身份验证SQL注入漏洞
android·数据库·sql·服务器安全
BXCQ_xuan35 分钟前
handsome主题美化及优化:10.1.0最新版 - 2
android
圈圈编码40 分钟前
MVVM框架
android·学习·kotlin
橙子199110163 小时前
在 Kotlin 中,什么是解构,如何使用?
android·开发语言·kotlin
androidwork4 小时前
Android 中使用通知(Kotlin 版)
android·kotlin
Digitally5 小时前
如何从 Android 设备打印短信(5 种方法)
android
casual_clover5 小时前
Android 中 打开文件选择器(ACTION_OPEN_DOCUMENT )
android
_龙小鱼_6 小时前
卡顿检测与 Choreographer 原理
android·kotlin
云手机管家7 小时前
账号风控突破:云手机设备指纹篡改检测与反制技术解析
android·运维·网络协议·网络安全·智能手机·矩阵·自动化