Android实现视频的画中画功能

【Android实现视频的画中画功能】简介: Android 8.0(API 级别 26)允许以画中画 (PIP) 模式启动 Activity。画中画是一种特殊类型的多窗口模式,最常用于视频播放。使用该模式,用户可以通过固定到屏幕一角的小窗口观看视频,同时在应用之间进行导航或浏览主屏幕上的内容。
画中画窗口会显示在屏幕的最上层,位于系统选择的一角。您可以将画中画窗口拖动到其他位置(会自动贴边)。当您点按该窗口时,会看到两个特殊的控件:全屏切换开关(位于窗口的中心)和关闭按钮(右上角的“X”)。
效果图:
Android实现视频的画中画功能
文章图片

1、声明对画中画的支持:
默认情况下,系统不会自动为应用提供画中画支持。如果您想在应用中支持画中画,可以通过将 android:supportsPictureInPicture 设置为 true,在清单中注册视频 Activity。此外,指定您的 Activity 处理布局配置更改,这样一来,在画中画模式转换期间发生布局更改时,您的 Activity 就不会重新启动。


2、将 Activity 切换到画中画模式:
如要进入画中画模式,Activity 必须调用enterPictureInPictureMode() 。
/** * Enters Picture-in-Picture mode. */void minimize() {if (null == mMovieView) {return; }// Hide the controls in Picture-in-Picture mode.mMovieView.hideControls(); // Calculate the aspect ratio of the PiP screen.Rational aspectRatio = new Rational(mMovieView.getWidth(), mMovieView.getHeight()); mPictureInPictureParamsBuilder.setAspectRatio(aspectRatio).build(); enterPictureInPictureMode(mPictureInPictureParamsBuilder.build()); }

3、处理画中画模式下的界面元素
当 Activity 进入或退出画中画模式时,系统会调用 Activity.onPictureInPictureModeChanged() 或 Fragment.onPictureInPictureModeChanged() 。在画中画模式下,Activity 会在一个小窗口中显示。在画中画模式下,用户无法与界面元素互动,并且可能很难看清小界面元素的详细信息。在 Activity 进入画中画模式之前移除其他界面元素,并在 Activity 再次变为全屏时恢复这些元素:
@Overridepublic void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); if (isInPictureInPictureMode) {// Starts receiving events from action items in PiP mode.mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {if (null == intent || !ACTION_MEDIA_CONTROL.equals(intent.getAction())) {return; }// This is where we are called back from Picture-in-Picture action items.final int controlType = intent.getIntExtra(EXTRA_CONTROL_TYPE, 0); switch (controlType) {case CONTROL_TYPE_PLAY:mMovieView.play(); break; case CONTROL_TYPE_PAUSE:mMovieView.pause(); break; default:break; }}}; registerReceiver(mReceiver, new IntentFilter(ACTION_MEDIA_CONTROL)); } else {// We are out of PiP mode. We can stop receiving events from it.unregisterReceiver(mReceiver); mReceiver = null; // Show the video controls if the video is not playingif (null != mMovieView && !mMovieView.isPlaying()) {mMovieView.showControls(); }}}

完整代码:
页面布局文件:

Activity文件:
public class VideoPipActivity extends AppCompatActivity {/*** Intent action for media controls from Picture-in-Picture mode.*/private static final String ACTION_MEDIA_CONTROL = "media_control"; /*** Intent extra for media controls from Picture-in-Picture mode.*/private static final String EXTRA_CONTROL_TYPE = "control_type"; /*** The request code for play action PendingIntent.*/private static final int REQUEST_PLAY = 1; /*** The request code for pause action PendingIntent.*/private static final int REQUEST_PAUSE = 2; /*** The request code for info action PendingIntent.*/private static final int REQUEST_INFO = 3; /*** The intent extra value for play action.*/private static final int CONTROL_TYPE_PLAY = 1; /*** The intent extra value for pause action.*/private static final int CONTROL_TYPE_PAUSE = 2; /*** The arguments to be used for Picture-in-Picture mode.*/private final PictureInPictureParams.Builder mPictureInPictureParamsBuilder =new PictureInPictureParams.Builder(); /*** This shows the video.*/private MovieView mMovieView; /*** The bottom half of the screen; hidden on landscape.*/private ScrollView mScrollView; /*** A {@link BroadcastReceiver} to receive action item events from Picture-in-Picture mode.*/private BroadcastReceiver mReceiver; private String mPlay; private String mPause; private final View.OnClickListener mOnClickListener = new View.OnClickListener() {@Overridepublic void onClick(View v) {if (v.getId() == R.id.pip) {minimize(); }}}; /*** Callbacks from the {@link MovieView} showing the video playback.*/private MovieView.MovieListener mMovieListener = new MovieView.MovieListener() {@Overridepublic void onMovieStarted() {// We are playing the video now. In PiP mode, we want to show an action item to// pause// the video.updatePictureInPictureActions(R.drawable.ic_pause_24dp, mPause, CONTROL_TYPE_PAUSE, REQUEST_PAUSE); }@Overridepublic void onMovieStopped() {// The video stopped or reached its end. In PiP mode, we want to show an action// item to play the video.updatePictureInPictureActions(R.drawable.ic_play_arrow_24dp, mPlay, CONTROL_TYPE_PLAY, REQUEST_PLAY); }@Overridepublic void onMovieMinimized() {// The MovieView wants us to minimize it. We enter Picture-in-Picture mode now.minimize(); }}; /*** Update the state of pause/resume action item in Picture-inPicture mode.** @param iconIdthe icon to be used.* @param titlethe title text.* @param controlType the type of te action. either {@link #CONTROL_TYPE_PLAY} or {@link #CONTROL_TYPE_PAUSE}.* @param requestCode The request code for the {@link PendingIntent}.*/void updatePictureInPictureActions(@DrawableRes int iconId, String title, int controlType, int requestCode) {final ArrayList actions = new ArrayList<>(); // This is the PendingIntent that is invoked when a user clicks on the item.// You need to use distinct request codes for play and pause, or the PendingIntent wont't// be properly updated.final PendingIntent intent = PendingIntent.getBroadcast(VideoPipActivity.this,requestCode,new Intent(ACTION_MEDIA_CONTROL).putExtra(EXTRA_CONTROL_TYPE, controlType),0); final Icon icon = Icon.createWithResource(VideoPipActivity.this, iconId); actions.add(new RemoteAction(icon, title, title, intent)); // Another action item. This is a fixed action.actions.add(new RemoteAction(Icon.createWithResource(VideoPipActivity.this, R.drawable.ic_info_24dp),getString(R.string.info),getString(R.string.info_description),PendingIntent.getActivity(VideoPipActivity.this,REQUEST_INFO,new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.info_uri))), 0))); mPictureInPictureParamsBuilder.setActions(actions); // This is how you can update action items (or aspect ratio) for Picture-in-Picture mode.// Note this call can happen even when the app is not in PiP mode. In that case, the// arguments will be used for at the next call of #enterPictureInPictureMode.setPictureInPictureParams(mPictureInPictureParamsBuilder.build()); }@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_pip); // Prepare string resource for Picture-in-Picture actions.mPlay = getString(R.string.play); mPause = getString(R.string.pause); // View referencesmMovieView = findViewById(R.id.movie); mScrollView = findViewById(R.id.scroll); // Set up the video; it automatically starts.mMovieView.setMovieListener(mMovieListener); findViewById(R.id.pip).setOnClickListener(mOnClickListener); }@Overrideprotected void onStop() {// On entering Picture-in-Picture mode, onPause is called, but not onStop.// For this reason, this is the place where we should pause the video playback.mMovieView.pause(); super.onStop(); }@Overrideprotected void onRestart() {super.onRestart(); if (!isInPictureInPictureMode()) {// Show the video controls so the video can be easily resumed.mMovieView.showControls(); }}@Overridepublic void onConfigurationChanged(@NonNull Configuration newConfig) {super.onConfigurationChanged(newConfig); adjustFullScreen(newConfig); }@Overridepublic void onWindowFocusChanged(boolean hasFocus) {super.onWindowFocusChanged(hasFocus); if (hasFocus) {adjustFullScreen(getResources().getConfiguration()); }}@Overridepublic void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); if (isInPictureInPictureMode) {// Starts receiving events from action items in PiP mode.mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {if (null == intent || !ACTION_MEDIA_CONTROL.equals(intent.getAction())) {return; }// This is where we are called back from Picture-in-Picture action items.final int controlType = intent.getIntExtra(EXTRA_CONTROL_TYPE, 0); switch (controlType) {case CONTROL_TYPE_PLAY:mMovieView.play(); break; case CONTROL_TYPE_PAUSE:mMovieView.pause(); break; default:break; }}}; registerReceiver(mReceiver, new IntentFilter(ACTION_MEDIA_CONTROL)); } else {// We are out of PiP mode. We can stop receiving events from it.unregisterReceiver(mReceiver); mReceiver = null; // Show the video controls if the video is not playingif (null != mMovieView && !mMovieView.isPlaying()) {mMovieView.showControls(); }}}/*** Enters Picture-in-Picture mode.*/void minimize() {if (null == mMovieView) {return; }// Hide the controls in Picture-in-Picture mode.mMovieView.hideControls(); // Calculate the aspect ratio of the PiP screen.Rational aspectRatio = new Rational(mMovieView.getWidth(), mMovieView.getHeight()); mPictureInPictureParamsBuilder.setAspectRatio(aspectRatio).build(); enterPictureInPictureMode(mPictureInPictureParamsBuilder.build()); }/*** Adjusts immersive full-screen flags depending on the screen orientation.** @param config The current {@link Configuration}.*/private void adjustFullScreen(Configuration config) {final View decorView = getWindow().getDecorView(); if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_FULLSCREEN| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); mScrollView.setVisibility(View.GONE); mMovieView.setAdjustViewBounds(false); } else {decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); mScrollView.setVisibility(View.VISIBLE); mMovieView.setAdjustViewBounds(true); }}}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    推荐阅读