Android实现视频的画中画功能

简介: Android 8.0(API 级别 26)允许以画中画 (PIP) 模式启动 Activity。画中画是一种特殊类型的多窗口模式,最常用于视频播放。使用该模式,用户可以通过固定到屏幕一角的小窗口观看视频,同时在应用之间进行导航或浏览主屏幕上的内容。

画中画窗口会显示在屏幕的最上层,位于系统选择的一角。您可以将画中画窗口拖动到其他位置(会自动贴边)。当您点按该窗口时,会看到两个特殊的控件:全屏切换开关(位于窗口的中心)和关闭按钮(右上角的“X”)。

效果图:

1、声明对画中画的支持:

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

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.csu.pictureinpicture">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">

        <activity
            android:name=".VideoPipActivity"
            android:resizeableActivity="true"
            android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
            android:supportsPictureInPicture="true" />
        <activity android:name=".MediaSessionPlaybackActivity"
            android:resizeableActivity="true"
            android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
            android:supportsPictureInPicture="true">

   <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

  </activity>

    </application>

</manifest>

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 再次变为全屏时恢复这些元素:

@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
    if (isInPictureInPictureMode) {
        // Starts receiving events from action items in PiP mode.
        mReceiver = new BroadcastReceiver() {
            @Override
            public 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 playing
        if (null != mMovieView && !mMovieView.isPlaying()) {
            mMovieView.showControls();
        }
    }
}

完整代码:

页面布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/activity_video"
    android:orientation="vertical"
    tools:context=".VideoPipActivity">

    <com.csu.pictureinpicture.widget.MovieView
        android:id="@+id/movie"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:adjustViewBounds="true"
        android:src="@raw/vid_bigbuckbunny"
        android:title="@string/title_bigbuckbunny"/>

    <ScrollView
        android:id="@+id/scroll"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <LinearLayout
            android:id="@+id/vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:paddingBottom="@dimen/activity_vertical_margin"
            android:paddingEnd="@dimen/activity_horizontal_margin"
            android:paddingStart="@dimen/activity_horizontal_margin"
            android:paddingTop="@dimen/activity_vertical_margin">

            <Button
                android:id="@+id/pip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/enter_picture_in_picture"/>

        </LinearLayout>

    </ScrollView>

</LinearLayout>

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() {
        @Override
        public 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() {
        @Override
        public 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);
        }

        @Override
        public 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);
        }

        @Override
        public 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 iconId      the icon to be used.
     * @param title       the 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<RemoteAction> 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());
    }

    @Override
    protected 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 references
        mMovieView = 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);
    }

    @Override
    protected 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();
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        if (!isInPictureInPictureMode()) {
            // Show the video controls so the video can be easily resumed.
            mMovieView.showControls();
        }
    }

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        adjustFullScreen(newConfig);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus) {
            adjustFullScreen(getResources().getConfiguration());
        }
    }

    @Override
    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
        super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
        if (isInPictureInPictureMode) {
            // Starts receiving events from action items in PiP mode.
            mReceiver = new BroadcastReceiver() {
                @Override
                public 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 playing
            if (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);
        }
    }

}

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

(0)

相关推荐

  • Android 8.0 中如何实现视频通话的画中画模式的示例

    Android 8.0 当中允许 Activiy 以画中画模式展现.这是一种多窗口模式的改进加强,在视频类应用中用处非常大,有了这种模式,就可以在视频通话或者观看直播的过程当中打开另外的应用而不用退出当前视频.更详细的就不再累述了,大家去阅读官方文档 就行 这里以 Agora SDK 为例来给大家展示下该特性,实际上不用 Agora SDK 做任何修改. 准备环境 Android 8.0 或以上版本手机 Agora SDK 1.14.0 或以上 版本 Android Studio 3.0 或以上

  • Android 画中画模式的实现示例

    画中画支持 Android 8.0(API 级别 26)允许以画中画模式启动 Activity.画中画是一种特殊类型的多窗口模式,最常用于视频播放.使用该模式,用户可以通过固定到屏幕一角的小窗口观看视频,同时在应用之间进行导航或浏览主屏幕上的内容. 画中画利用 Android 7.0 中的多窗口模式 API 来提供固定的视频叠加窗口.要将画中画添加到您的应用中,您需要注册支持画中画的 Activity.根据需要将 Activity 切换为画中画模式,并确保当 Activity 处于画中画模式时,

  • Android实现画中画功能(图片)

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

  • Android实现视频的画中画功能

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

  • Android 破解视频App去除广告功能详解及解决办法总结

    Android 破解视频App去除广告功能 作为一个屌丝程序猿也有追剧的时候,但是当打开视频app的时候,那些超长的广告已经让我这个屌丝无法忍受了,作为一个程序猿看视频还要出现广告那就是打我脸,但是我有没有钱买会员,只能靠着毕生技能去耍耍去除广告了.下面就来介绍一下如何进行视频广告的去除. 一.视频广告播放原理 首先我们需要了解的一个基本知识点那就是广告其实也是一段视频,那么他肯定有请求地址和播放地址.那么我们的思路就来了,如果能够得到这些地址的话,我们就可以去除广告了,为什么呢?因为我们知道所

  • Android实现简单画中画功能

    Android 8.0推出了PictureInPicture(画中画功能),目前只有在8.0以上的系统上支持.对比IOS,IOS的Picture in Picture 模式是苹果公司在 iOS 9 中加入的一项多任务功能.下面先看一下效果: 相信不少人在平时使用ios手机的app时,已经体验过了,很高兴谷歌也推出了这项功能. 使用画中画模式注意点: 1.要使用画中画模式的Activity需要在清单文件中添加属性: android:configChanges="screenSize|smalles

  • Android实现发送短信功能实例详解

    本文实例分析了Android实现发送短信功能的方法.分享给大家供大家参考,具体如下: 短信和打电话一样,都是android手机的基本功能,下面以实例说明android如何实现发送短信的功能. 程序如下所示: import java.util.regex.Matcher; import java.util.regex.Pattern; import android.app.Activity; import android.app.PendingIntent; import android.cont

  • Android开发中滑动分页功能实例详解

    本文实例讲述了Android开发中滑动分页功能.分享给大家供大家参考,具体如下: android UI 往右滑动,滑动到最后一页就自动加载数据并显示 如图: Java代码: package cn.anycall.ju; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.app.Activity; import andro

  • Android编程实现自定义title功能示例

    本文实例讲述了Android编程实现自定义title功能.分享给大家供大家参考,具体如下: 这里我在前面加了个logo,而且改变了title的背景和高度. 首先编写title的布局文件,title.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  • Android编程实现手机震动功能的方法

    本文实例讲述了Android编程实现手机震动功能的方法.分享给大家供大家参考,具体如下: 在与用户交互时,常常会用到震动功能,以提醒用户.该功能实现比较简单,请参阅下面主要代码: import android.app.Activity; import android.app.Service; import android.os.Vibrator; public class TipHelper { public static void Vibrate(final Activity activity

  • Android编程实现播放MP3功能示例

    本文实例讲述了Android编程实现播放MP3功能.分享给大家供大家参考,具体如下: 在android中播放mp3非常简单,也是项目中经常使用的,比如说要做项目的背景音乐,应用中某些功能的提示音等的.应用非常广泛,下面提供一个简单的使用实例: layout文件的配置: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.and

  • Android中 视频屏幕左半部分上下滑动改变亮度右半部分上下滑动改变声音

    说明: 实现功能: (1)屏幕右半部分上滑,声音变大,下滑,声音变小 屏幕左半部分上滑,亮度变大,下滑,亮度变小 (2)如果亮度>1或者小于0.2时,手机震动 private float startY;//记录手指按下时的Y坐标 private float startX;//记录手指按下时的Y坐标 private int downVol;//记录手指按下时的音量 private Vibrator vibrator;//手机震动器 //不要忘记震动权限<uses-permission andro

  • Android编程模拟HOME键功能示例

    本文实例讲述了Android编程模拟HOME键功能的方法.分享给大家供大家参考,具体如下: 做一个类似于QQ按返回键并不销毁Activity的方法(即不调用Activity.finish(),系统不调用 onDestroy),而是类似于按Home键,让Activity类似于"暂停"(即只调用onPause,onDestroy). 代码如下: public boolean onKeyDown(int keyCode, KeyEvent event) { if(keyCode == Key

随机推荐