文章目录
最简单的demo
1.AndroidManifest.xml
中Activity
增加 android:supportsPictureInPicture="true"
,如下
<activity android:name=".MainActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:supportsPictureInPicture="true">
页面从全屏模式切换到画中画模式,Activity生命周期也会经历销毁后重建的过程,如果开发者想保持页面不被重建,则需要加上android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
smallestScreenSize|screenLayout
这两个会影响到在PIP
模式下action
的响应,比如测试发现,如果没有smallestScreenSize|screenLayout
这两个,BroadcastReceiver
收不到广播的消息!
2.为主窗口增加一个按钮:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<Button android:id="@+id/pip_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击进入PIP">
</Button>
</LinearLayout>
3.点击按钮事件中调用public boolean enterPictureInPictureMode(PictureInPictureParams params)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.pip_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
testPIP();
}
});
}
void testPIP() {
if (Build.VERSION.SDK_INT >= 26) {
PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();
enterPictureInPictureMode(builder.build());
}
}
}
效果如下:
PIP相关函数简介
enterPictureInPictureMode (PictureInPictureParams args)
进入画中画模式。操作组件的纵横比和其他配置设置均由 args
指定。如果 args
中的任何字段为空,系统将使用您上次调用 setPictureInPictureParams()
时所设置的值。
PictureInPictureParams
Android 8.0(API Level 26) 引入了一个新的对象 PictureInPictureParams
,它可以指定PIP的一些特定属性
PictureInPictureParams.setAspectRatio
第一个参数为分子,第二个为分母,指定宽高比,必须在 2.39:1
或1:2.39
之间,否则会抛出IllegalArgumentException
异常。
我们将前面的testPIP
稍做修改一下,设置宽高比为2:1
void testPIP() {
if (Build.VERSION.SDK_INT >= 26) {
PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();
builder.setAspectRatio(new Rational(2,1));// 宽高比2:1
enterPictureInPictureMode(builder.build());
}
}
效果如下:可以看到坚屏和横屏切换时,PIP窗口相应做了变化
PictureInPictureParams.setActions
在PIP
中增加一组控制按钮RemoteAction
,点击这个控制按钮进入某种操作,比如广播消息
public RemoteAction(@NonNull Icon icon, @NonNull CharSequence title,
@NonNull CharSequence contentDescription, @NonNull PendingIntent intent) {
参数为:icon、标题、描述、点击图标的反应
我们将前面的testPIP
稍做修改一下, 让Action
点击后跳到主页面:
void testPIP() {
if (Build.VERSION.SDK_INT >= 26) {
PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();
builder.setAspectRatio(new Rational(2,1));// 宽高比2:1
RemoteAction remoteAction = new RemoteAction(Icon.createWithResource(this, R.mipmap.ic_launcher),
"标题", "描述",
PendingIntent.getActivity(this, 100, getIntent(), PendingIntent.FLAG_UPDATE_CURRENT));
final ArrayList<RemoteAction> actions = new ArrayList<>();
actions.add(remoteAction);
builder.setActions(actions);
enterPictureInPictureMode(builder.build());
}
}
实现后会增加一个小按钮
,按钮的图标就是 代码中指定的
R.mipmap.ic_launcher
。
效果如下:
setPictureInPictureParams
更新画中画活动的属性,或将其设置为稍后在enterPictureInPictureMode()
调用时使用
isInPictureInPictureMode
isInPictureInPictureMode
返回true
表示处于画中画模式,返回false
表示处于全屏模式。
onPictureInPictureModeChanged
每当进入或退出PIP
模式,都会触发Activity页面的onPictureInPictureModeChanged
方法。通过重载该方法,应用可以实时收到画中画与全屏的切换通知,并在此控制控件的展示。
PIP注意点
当一个Activity在画中画模式,它处于暂停
状态,但可能需要继续展示内容(如视频播放)。出于这个原因,如果是一个视频播放程序,不能在 onpause
中暂停播放。相反,您应该在 onstop
中暂停播放·,在 onstart
中继续播放。
PIP详细的demo
扩展上面的demo, 增加一个Action
用来广播消息,每次广播把controlType
增加1,代码如下:
public class MainActivity extends Activity {
private static final String ACTION_BROADCAST_CONTROL = "broadcast_control";
private static final String EXTRA_CONTROL_TYPE = "control_type";
private PictureInPictureParams.Builder mPictureInPictureParamsBuilder = new PictureInPictureParams.Builder();
private BroadcastReceiver mReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
updatePIPActions(R.mipmap.ic_launcher, "描述", 100, 100);
findViewById(R.id.pip_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
testPIP();
}
});
}
@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
if (isInPictureInPictureMode) {
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null || !ACTION_BROADCAST_CONTROL.equals(intent.getAction())) {
return;
}
//这就是我们从画中画模式的操作回调的地方
int controlType = intent.getIntExtra(EXTRA_CONTROL_TYPE, 0);
Toast.makeText(MainActivity.this, "控制码" + controlType, Toast.LENGTH_SHORT).show();
controlType ++;
updatePIPActions(R.mipmap.ic_launcher, "描述", controlType, controlType);
}
};
registerReceiver(mReceiver, new IntentFilter(ACTION_BROADCAST_CONTROL));
} else {
// 当我们不在画中画模式时,停止接收广播
unregisterReceiver(mReceiver);
mReceiver = null;
}
}
void testPIP() {
if (Build.VERSION.SDK_INT >= 26) {
mPictureInPictureParamsBuilder.setAspectRatio(new Rational(2, 1)).build();// 宽高比2:1
enterPictureInPictureMode(mPictureInPictureParamsBuilder.build());
}
}
// 注意当controlType改变时,requestCode也必须要变,同一个requestCode会使用第一次关联的controlType
void updatePIPActions(int iconId, String title, int controlType, int requestCode) {
final ArrayList<RemoteAction> actions = new ArrayList<>();
Intent intent = new Intent(ACTION_BROADCAST_CONTROL);
intent.putExtra(EXTRA_CONTROL_TYPE, controlType);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, requestCode, intent, 0);
final Icon icon = Icon.createWithResource(this, iconId);
actions.add(new RemoteAction(icon, title, title, pendingIntent));
mPictureInPictureParamsBuilder.setActions(actions);
setPictureInPictureParams(mPictureInPictureParamsBuilder.build());
}
}
效果如下: