使用子母畫面 (PiP) 新增影片

從 Android 8.0 (API 級別 26) 開始,Android 允許在 子母畫面 (PiP) 模式。子母畫面是一種特殊的多視窗模式,主要用於 用於影片播放。讓使用者透過固定的小視窗觀看影片 應用程式、 主畫面。

子母畫面會利用 Android 7.0 的多視窗 API 來提供 固定的影片重疊視窗。如要為應用程式新增子母畫面功能,您必須註冊 活動 (支援子母畫面模式)、視需要將活動切換至子母畫面模式。 確認使用者介面元素處於隱藏狀態,並在活動活動時繼續播放影片 目前為子母畫面模式

子母畫面視窗會顯示在畫面最頂層的角落,並依照 以及系統

搭載相容 Android TV OS 的相容 Android TV OS 裝置也支援子母畫面模式 Android 14 (API 級別 34) 以上版本。雖然有許多相似之處 使用資料集時 電視版 PiP

使用者如何與子母畫面視窗互動

使用者可以將子母畫面視窗拖曳至其他位置。自 Android 12 起,使用者 也可以:

  • 輕觸一下視窗即可顯示全螢幕切換鈕、關閉按鈕、 設定按鈕,以及應用程式提供的自訂動作 (例如 Google Play 控制項)。

  • 輕觸兩下視窗,在目前的子母畫面大小和最大值之間切換 或最小子母畫面尺寸 (例如:輕觸兩下最大化視窗) 反之亦然

  • 將視窗拖曳到左側或右側邊緣,即可隱藏視窗。揭開 則輕觸隱藏視窗的可見部分,或將視窗拉出。

  • 使用雙指撥動方式進行縮放,調整子母畫面視窗的大小。

應用程式可控制目前活動進入子母畫面模式的時機,以下是一些 範例:

  • 使用者輕觸主畫面按鈕或滑動時,活動就能進入子母畫面模式 最多人在家這就是 Google 地圖在持續顯示路線的方式 使用者同時執行其他活動。

  • 當使用者離開背景時,應用程式可將影片移至子母畫面模式 影片就能瀏覽其他內容

  • 當使用者看完影片時,應用程式可將影片切換至子母畫面模式 第一集的內容主畫面顯示促銷內容或摘要 系列叢書中下一集的資訊

  • 應用程式可讓使用者將其他內容排入佇列, 觀看影片時影片在主要畫面時,會繼續在子母畫面模式下播放 螢幕上會顯示內容選取活動。

宣告子母畫面支援

根據預設,系統不會自動為應用程式提供子母畫面支援功能。如果您希望 支援應用程式的子母畫面模式,請 將 android:supportsPictureInPicture 設為 true。此外,請指明 活動會處理版面配置設定變更,因此活動不會 在子母畫面模式轉換期間版面配置變更時重新啟動。

<activity android:name="VideoActivity"
    android:supportsPictureInPicture="true"
    android:configChanges=
        "screenSize|smallestScreenSize|screenLayout|orientation"
    ...

將活動切換至子母畫面模式

從 Android 12 開始,你可以透過設定將活動切換至子母畫面模式 將 setAutoEnterEnabled 標記套用至 true。選用這項設定後,系統會將活動 不需要明確呼叫,系統就會視需要自動切換至子母畫面模式 onUserLeaveHint 中的enterPictureInPictureMode()。這個例子中有 好處多多了。詳情請參閱 透過手勢操作更順暢地轉換至子母畫面模式

如果您指定 Android 11 以下版本,活動就必須呼叫 enterPictureInPictureMode() 並切換至子母畫面模式。舉例來說,以下程式碼會將活動切換至 當使用者按一下應用程式 UI 中的專屬按鈕時,子母畫面模式:

Kotlin

override fun onActionClicked(action: Action) {
    if (action.id.toInt() == R.id.lb_control_picture_in_picture) {
        activity?.enterPictureInPictureMode()
        return
    }
}

Java

@Override
public void onActionClicked(Action action) {
    if (action.getId() == R.id.lb_control_picture_in_picture) {
        getActivity().enterPictureInPictureMode();
        return;
    }
    ...
}

建議您加入相關邏輯,將活動切換至子母畫面模式 把工作做好舉例來說,如果發生下列情況,Google 地圖會切換至子母畫面模式: 使用者在應用程式進行導航時,按下主畫面或「最近」按鈕。你可以 以擷取此案件 onUserLeaveHint()

Kotlin

override fun onUserLeaveHint() {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode()
    }
}

Java

@Override
public void onUserLeaveHint () {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode();
    }
}

建議做法:為使用者提供優質的子母畫面轉換體驗

Android 12 大幅改善了動畫轉場效果 全螢幕和子母畫面視窗之間執行。強烈建議您全都導入 適用的變更;完成上述步驟後,這些變更會自動調整成 折疊式裝置和平板電腦等大螢幕裝置,您無需進行其他操作。

如果您的應用程式未提供適用的更新,子母畫面轉換作業仍會持續 但動畫較不精緻例如,將 從全螢幕到子母畫面模式,可能導致子母畫面視窗在播放期間消失 轉換。

這些異動涉及以下資源。

  • 透過手勢操作讓切換子母畫面模式更順暢
  • 針對進入及退出子母畫面模式設定適當的 sourceRectHint
  • 停用非影片內容的流暢大小調整功能

請參閱 Android Kotlin PictureInPicture 範例 做為最終轉換體驗的參考依據。

透過手勢操作,讓轉換至子母畫面模式的過程更加順暢

從 Android 12 開始,setAutoEnterEnabled 標記提供了許多 使用手勢轉換至子母畫面模式時,動畫更流暢的動畫 導航,例如從全螢幕模式向上滑動到主畫面。

請完成下列步驟進行這項變更,並參考這個範例, 參考資源:

  1. 使用 setAutoEnterEnabled 建構 PictureInPictureParams.Builder

    Kotlin

    setPictureInPictureParams(PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build())
    

    Java

    setPictureInPictureParams(new PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build());
    
  2. 呼叫 setPictureInPictureParams 並提供最新版本 提早 PictureInPictureParams。應用程式不會等待 onUserLeaveHint 回呼 (就像在 Android 11 中一樣)。

    舉例來說,您可以在setPictureInPictureParams

  3. 請視需要呼叫 setAutoEnterEnabled(false)。例如: 如果目前的播放作業處於暫停狀態,建議您不要進入子母畫面模式 state.

為進入及退出子母畫面模式設定適當的 sourceRectHint

從 Android 8.0 版開始推出子母畫面,setSourceRectHint 表示轉場後顯示的活動區域 子母畫面 - 例如影片觀看次數在影片播放器中的邊界。

在 Android 12 中,系統使用 sourceRectHint 實作更加順暢 進入及結束子母畫面模式時顯示動畫。

如何為進入及退出子母畫面模式正確設定 sourceRectHint

  1. 建造 PictureInPictureParams 使用適當的邊界做為 sourceRectHint。建議您一併附上 影片播放器的版面配置變更監聽器:

    Kotlin

    val mOnLayoutChangeListener =
    OnLayoutChangeListener { v: View?, oldLeft: Int,
            oldTop: Int, oldRight: Int, oldBottom: Int, newLeft: Int, newTop:
            Int, newRight: Int, newBottom: Int ->
        val sourceRectHint = Rect()
        mYourVideoView.getGlobalVisibleRect(sourceRectHint)
        val builder = PictureInPictureParams.Builder()
            .setSourceRectHint(sourceRectHint)
        setPictureInPictureParams(builder.build())
    }
    
    mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener)
    

    Java

    private final View.OnLayoutChangeListener mOnLayoutChangeListener =
            (v, oldLeft, oldTop, oldRight, oldBottom, newLeft, newTop, newRight,
            newBottom) -> {
        final Rect sourceRectHint = new Rect();
        mYourVideoView.getGlobalVisibleRect(sourceRectHint);
        final PictureInPictureParams.Builder builder =
            new PictureInPictureParams.Builder()
                .setSourceRectHint(sourceRectHint);
        setPictureInPictureParams(builder.build());
    };
    
    mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener);
    
  2. 視需要在系統啟動系統前更新 sourceRectHint 退出轉場效果當系統即將退出子母畫面模式時,活動的 檢視區塊階層會配置其目的地設定 (例如 全螢幕播放)。應用程式可將版面配置變更事件監聽器附加至根檢視區塊 或目標檢視 (例如影片播放器視圖) 來偵測事件 請在動畫開始前更新 sourceRectHint

    Kotlin

    // Listener is called immediately after the user exits PiP but before animating.
    playerView.addOnLayoutChangeListener { _, left, top, right, bottom,
                        oldLeft, oldTop, oldRight, oldBottom ->
        if (left != oldLeft
            || right != oldRight
            || top != oldTop
            || bottom != oldBottom) {
            // The playerView's bounds changed, update the source hint rect to
            // reflect its new bounds.
            val sourceRectHint = Rect()
            playerView.getGlobalVisibleRect(sourceRectHint)
            setPictureInPictureParams(
                PictureInPictureParams.Builder()
                    .setSourceRectHint(sourceRectHint)
                    .build()
            )
        }
    }
    
    

    Java

    // Listener is called right after the user exits PiP but before
    // animating.
    playerView.addOnLayoutChangeListener((v, left, top, right, bottom,
                        oldLeft, oldTop, oldRight, oldBottom) -> {
        if (left != oldLeft
            || right != oldRight
            || top != oldTop
            || bottom != oldBottom) {
            // The playerView's bounds changed, update the source hint rect to
            // reflect its new bounds.
            final Rect sourceRectHint = new Rect();
            playerView.getGlobalVisibleRect(sourceRectHint);
            setPictureInPictureParams(
                new PictureInPictureParams.Builder()
                    .setSourceRectHint(sourceRectHint)
                    .build());
        }
    });
    
    

停用非影片內容的流暢大小調整功能

Android 12 新增了 setSeamlessResizeEnabled 標記,可提供許多 為子母畫面中的非影片內容調整大小時,更流暢的交錯淡出動畫效果 視窗。以往,為子母畫面視窗中的非影片內容調整大小時, 不突兀的視覺構件

如何停用非影片內容的流暢大小調整功能:

Kotlin

setPictureInPictureParams(PictureInPictureParams.Builder()
    .setSeamlessResizeEnabled(false)
    .build())

Java

setPictureInPictureParams(new PictureInPictureParams.Builder()
    .setSeamlessResizeEnabled(false)
    .build());

在子母畫面期間處理 UI

當活動進入或結束子母畫面模式時,系統會呼叫 Activity.onPictureInPictureModeChanged()Fragment.onPictureInPictureModeChanged().

您應該要覆寫這些回呼,以重新繪製活動的 UI 元素。保留 請注意,在子母畫面模式下,活動會顯示在小型視窗中。使用者無法 並在子母畫面模式下與應用程式的 UI 元素互動。 就算是微小的 UI 元素,可能也不易察覺含有 可提供最佳使用者體驗。

如果您的應用程式需要針對子母畫面模式提供自訂動作,請參閱新增 控制項。在 活動會進入子母畫面模式,並在活動變為全螢幕模式時還原 :

Kotlin

override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean,
                                           newConfig: Configuration) {
    if (isInPictureInPictureMode) {
        // Hide the full-screen UI (controls, etc.) while in PiP mode.
    } else {
        // Restore the full-screen UI.
    }
}

Java

@Override
public void onPictureInPictureModeChanged (boolean isInPictureInPictureMode, Configuration newConfig) {
    if (isInPictureInPictureMode) {
        // Hide the full-screen UI (controls, etc.) while in PiP mode.
    } else {
        // Restore the full-screen UI.
        ...
    }
}

新增控制項

當使用者開啟視窗的選單 (方法是 在行動裝置上輕觸視窗,或從電視上選取選單 。)

如果應用程式有主動式媒體 然後播放 就會顯示暫停、下一個和上一個控制項

您也可以建立 PictureInPictureParams 同時 PictureInPictureParams.Builder.setActions() 再進入子母畫面模式,並在進入子母畫面模式時,使用 enterPictureInPictureMode(android.app.PictureInPictureParams)setPictureInPictureParams(android.app.PictureInPictureParams)。 小心一點,如果您嘗試新增 getMaxNumPictureInPictureActions(), 只會取得上限值

在子母畫面模式下繼續播放影片

當活動切換至子母畫面模式時,系統會將活動設為暫停狀態 並呼叫活動的 onPause() 方法,增加圍繞地圖邊緣的邊框間距。視訊 在活動 轉換至子母畫面模式時暫停。

在 Android 7.0 以上版本中,您應該在 系統會呼叫您活動的 onStop()onStart().這麼做 您就不必在 onPause() 中檢查應用程式是否處於子母畫面模式,並 明確繼續播放。

如果您尚未將 setAutoEnterEnabled 標記設為 true,且需要 在 onPause() 實作中暫停播放,請呼叫 isInPictureInPictureMode() 並妥善處理播放。例如:

Kotlin

override fun onPause() {
    super.onPause()
    // If called while in PiP mode, do not pause playback
    if (isInPictureInPictureMode) {
        // Continue playback
    } else {
        // Use existing playback logic for paused Activity behavior.
    }
}

Java

@Override
public void onPause() {
    // If called while in PiP mode, do not pause playback
    if (isInPictureInPictureMode()) {
        // Continue playback
        ...
    } else {
        // Use existing playback logic for paused Activity behavior.
        ...
    }
}

當活動從子母畫面模式切換回全螢幕模式時,系統會 恢復活動並撥打電話給 onResume() 方法,增加圍繞地圖邊緣的邊框間距。

針對子母畫面模式使用單一播放活動

在您的應用程式中,使用者可能在瀏覽 主畫面。播放新影片 處於全螢幕模式的現有播放活動中,而不是啟動 可能會混淆使用者的新活動

確保影片播放要求會使用單一活動,並切換 視需要進入或退出子母畫面模式,請將活動的 android:launchMode 設為 資訊清單中的 singleTask

<activity android:name="VideoActivity"
    ...
    android:supportsPictureInPicture="true"
    android:launchMode="singleTask"
    ...

在活動中覆寫 onNewIntent() 處理新影片,並視需要停止任何現有的影片播放作業。

最佳做法

在 RAM 較少的裝置上,系統可能會停用子母畫面模式。在應用程式使用子母畫面模式前, 請撥打電話,確認是否能正常使用服務。 hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE).

子母畫面功能適用於播放全螢幕影片的活動。改用 設為子母畫面模式,請不要顯示影片以外的內容。追蹤時機 您的活動會進入子母畫面模式並隱藏 UI 元素,如「處理使用者介面」所述 期間

根據預設,活動處於子母畫面模式時不會取得輸入焦點。如要在子母畫面模式下接收輸入事件,請使用 MediaSession.setCallback()。如要進一步瞭解如何使用 setCallback(),請參閱「顯示聽聲辨曲」 資訊卡

應用程式處於子母畫面模式時,在子母畫面視窗中播放影片可能會造成音訊 幹擾其他應用程式,例如音樂播放器應用程式或語音搜尋應用程式。 為避免這種情況,請在開始播放影片時要求音訊焦點。 音訊焦點變更通知,詳情請參閱「管理音訊」一節 焦點。如果收到通知 的音量。

請注意,當應用程式要進入子母畫面模式時,只有上層活動會進入這個模式 子母畫面。在某些情況 (例如多視窗裝置) 中 下方活動將重新顯示,並會 子母畫面活動請根據實際情形處理這種狀況,包括 下方活動取得 onResume()onPause() 回呼。這個 可能會與活動互動。舉例來說 顯示影片清單活動,以及在子母畫面模式下播放影片活動, 使用者即可從清單中選取新影片,這時子母畫面活動應該就會更新 。

其他程式碼範例

如要下載以 Kotlin 編寫的範例應用程式,請參閱「Android PictureInPicture 範例 (Kotlin)