本文介绍如何在Android端实现画中画(悬浮窗)。
功能介绍
“画中画”(Picture-in-Picture,简称 PiP)是一种让视频“悬浮”在屏幕上的功能。开启后,视频会以一个小窗口的形式显示在屏幕一角,用户可以一边看视频,一边正常使用手机或电脑的其他应用,比如回消息、浏览网页或操作其他App,互不干扰。
示例代码
ARTC 提供了开源示例代码供您参考:Android实现画中画。
前提条件
要实现画中画功能,需要满足如下条件:
Android 版本不能低于 Android 8.0(API 级别 26)。
功能实现
1. 配置相关属性
画中画的 API 维度为 Activity,因此需要在 AndroidManifest.xml 中为 Activity 声明下列属性
android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
<activity android:name=".PictureInPicture.PictureInPictureAcitivity"
android:label="@string/picture_in_picture"
android:exported="false"
android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:resizeableActivity="true"
android:launchMode="singleTask"
tools:targetApi="24" />
2. 进入画中画模式
部分设备可能无法使用画中画模式,因此进入画中画模式前需要调用
hasSystemFeature
接口进行检查。调用系统 API
enterPictureInPictureMode
进入画中画模式。
private void enterPIPMode(){
if(!hasJoined) {
ToastHelper.showToast(this, "请先加入频道", Toast.LENGTH_SHORT);
return;
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if(getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
Rational aspectRatio = new Rational(9, 16); // 推荐视频比例
PictureInPictureParams params = new PictureInPictureParams.Builder()
.setAspectRatio(aspectRatio)
.build();
enterPictureInPictureMode(params);
} else {
ToastHelper.showToast(this, "设备不支持画中画", Toast.LENGTH_SHORT);
}
} else {
ToastHelper.showToast(this, "Android 8.0 以上才支持画中画", Toast.LENGTH_SHORT);
}
}
3. 响应画中画变化
可以重写系统onPictureInPictureModeChanged
回调并根据业务场景控制 UI 变化。
@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
if (isInPictureInPictureMode) {
// 进入 PIP:隐藏 全屏UI,只保留视频
findViewById(R.id.ll_channel_layout).setVisibility(View.GONE);
findViewById(R.id.scroll_remote_container).setVisibility(View.GONE);
// 可选:隐藏状态栏
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
// 可选:创建专用 PIP 视频容器
showPipVideoContainer();
} else {
// 退出 PIP:恢复 全屏UI
findViewById(R.id.ll_channel_layout).setVisibility(View.VISIBLE);
findViewById(R.id.scroll_remote_container).setVisibility(View.VISIBLE);
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
// 重新绑定本地预览视图
if (mAliRtcEngine != null && mLocalVideoCanvas != null) {
mAliRtcEngine.setLocalViewConfig(mLocalVideoCanvas, AliRtcVideoTrackCamera);
}
// 移除 PIP 容器
removePipVideoContainer();
}
}
/**
* 显示 PIP 模式下的主视频容器(例如本地预览)
*/
private void showPipVideoContainer() {
if (mPipVideoContainer == null) {
mPipVideoContainer = new FrameLayout(this);
mPipVideoContainer.setBackgroundColor(Color.BLACK);
addContentView(mPipVideoContainer, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
// 重新创建本地预览视图(不能移动 SurfaceView,只能重建)
SurfaceView surfaceView = mAliRtcEngine.createRenderSurfaceView(this);
surfaceView.setZOrderOnTop(true);
surfaceView.setZOrderMediaOverlay(true);
mPipVideoContainer.addView(surfaceView, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
if(mAliRtcEngine != null) {
AliRtcEngine.AliRtcVideoCanvas canvas = new AliRtcEngine.AliRtcVideoCanvas();
canvas.view = surfaceView;
mAliRtcEngine.setLocalViewConfig(canvas, AliRtcVideoTrackCamera);
}
}
}
/**
* 移除 PIP 视频容器
*/
private void removePipVideoContainer() {
if (mPipVideoContainer != null && mPipVideoContainer.getParent() != null) {
((ViewGroup) mPipVideoContainer.getParent()).removeView(mPipVideoContainer);
mPipVideoContainer = null;
}
}
该文章对您有帮助吗?