日本熟妇hd丰满老熟妇,中文字幕一区二区三区在线不卡 ,亚洲成片在线观看,免费女同在线一区二区

如何實現(xiàn)懸浮窗和畫中畫功能

您可以在應(yīng)用層調(diào)用播放器SDK,結(jié)合系統(tǒng)API實現(xiàn)懸浮窗、畫中畫播放功能。本文為您介紹如何在Android和iOS平臺上實現(xiàn)播放器SDK的懸浮窗、畫中畫播放功能。

Android技術(shù)方案

Android系統(tǒng)提供了多種不同的方案供您選擇,包括懸浮窗、畫中畫方案,例如電商場景下常見的浮窗方案。此處僅介紹常見的實現(xiàn)方法。

懸浮窗

懸浮窗是Android系統(tǒng)中的一種浮動窗口,可以在其他應(yīng)用程序的上層顯示。它可以進行隨意拖動、縮放、關(guān)閉等操作,通常用于提醒、通知和廣告。

  • 在Android系統(tǒng)中,每個窗口都對應(yīng)一個Window對象,而懸浮窗就是一種特殊的Window,通常情況下,懸浮窗可以通過ViewSystem中的PopupWindow來實現(xiàn)。

  • 其中,PopupWindow是繼承自具有運動能力的WindowManager.LayoutParams的一個類,因此可以對其進行位置、大小和顯示方式等操作。同時,可以使用PopupWindow實現(xiàn)一個自定義懸浮窗,而不影響其他應(yīng)用,并且無需通過Activity跳轉(zhuǎn)。

實現(xiàn)步驟如下:

  1. 在AndroidManifest.xml中聲明懸浮窗權(quán)限。

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
  2. 在需要顯示懸浮窗的Activity或Service中,通過創(chuàng)建WindowManager和PopupWindow,設(shè)置其顯示位置、大小和內(nèi)容等屬性。

    //創(chuàng)建布局
    View layout = View.inflate(this, R.layout.float_window, null);
    //創(chuàng)建管理器
    WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
    //創(chuàng)建懸浮窗
    PopupWindow mPopupWindow = new PopupWindow(layout,
                                               WindowManager.LayoutParams.WRAP_CONTENT,
                                               WindowManager.LayoutParams.WRAP_CONTENT);
    //設(shè)置顯示位置
    mPopupWindow.showAtLocation(parentView, Gravity.LEFT | Gravity.TOP, x, y);
    //設(shè)置可點擊和可獲取焦點
    mPopupWindow.setTouchable(true);
    mPopupWindow.setFocusable(true);
    //設(shè)置背景色
    mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
  3. 設(shè)置懸浮窗布局內(nèi)控件的點擊事件、拖拽監(jiān)聽事件等。

    mPopupWindow.setOnTouchListener(new View.OnTouchListener() {
        int lastX, lastY;
        int paramX, paramY;
    
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            //獲取當前觸摸點相對于屏幕的坐標
            int x = (int) event.getRawX();
            int y = (int) event.getRawY();
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    lastX = x;
                    lastY = y;
                    paramX = mPopupWindow.getLayoutParams().x;
                    paramY = mPopupWindow.getLayoutParams().y;
                    break;
                case MotionEvent.ACTION_MOVE:
                    int dx = x - lastX;
                    int dy = y - lastY;
                    mPopupWindow.update(paramX + dx, paramY + dy, -1, -1);
                    break;
            }
            return false;
        }
    });
    重要

    在Android 6.0及以上版本中,需要動態(tài)申請懸浮窗權(quán)限。然而,即使如此,在某些Android 8.0手機上,仍然不支持此功能。

畫中畫

  • 從Android 8.0(API 26)開始,Android引入了畫中畫(PiP)模式啟動Activity的功能。畫中畫是一種特殊類型的多窗口模式,最常用于視頻播放。在該模式下,用戶可以將視頻以小窗口的形式固定在屏幕的一角,同時在應(yīng)用之間進行導(dǎo)航或瀏覽主屏幕上的內(nèi)容。

  • 畫中畫利用Android 7.0中提供的多窗口模式API來實現(xiàn)固定的視頻疊加窗口。為了在應(yīng)用中添加畫中畫功能,您需要注冊支持畫中畫的Activity,并根據(jù)需要將該Activity切換為畫中畫模式,同時,確保當Activity處于畫中畫模式時,界面元素處于隱藏狀態(tài)且視頻能夠繼續(xù)播放。

  • 畫中畫窗口會顯示在屏幕的最上層,通常位于系統(tǒng)選擇的一角。

  • 有關(guān)Android系統(tǒng)對畫中畫功能的支持情況請參見:Android · 對畫中畫 (PiP) 的支持

實現(xiàn)步驟如下:

  1. 在AndroidManifest.xml中聲明Activity對畫中畫的支持。

    <Activity android:name="VideoActivity"
      android:supportsPictureInPicture="true"
      android:configChanges=
      "screenSize|smallestScreenSize|screenLayout|orientation"
      ...
    • 默認情況下,Android系統(tǒng)不會自動為應(yīng)用提供畫中畫功能的支持。如果您想在應(yīng)用中支持畫中畫功能,可以在AndroidManifest.xml清單文件中注冊視頻Activity,并將android:supportsPictureInPicture屬性設(shè)置為true。

    • 另外,在注冊支持畫中畫的Activity時,還需要指定該Activity來處理布局配置更改。這樣,在畫中畫模式切換期間,如果出現(xiàn)布局更改,您的Activity將不會重新啟動,從而提供更加流暢的用戶體驗。

      重要

      低RAM設(shè)備可能無法使用畫中畫模式。在應(yīng)用使用畫中畫之前,請務(wù)必通過調(diào)用hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)進行檢查,以確保可以使用畫中畫。

  2. 將Activity切換到畫中畫模式。

    進入PIP模式的最常見流程如下:

    • 從按鈕觸發(fā)(例如通過點擊按鈕)

      onClicked(View),onOptionsItemSelected(MenuItem) 等等。

      @Override
      public void onActionClicked(Action action) {
          if (action.getId() == R.id.lb_control_picture_in_picture) {
          	// 從按鈕觸發(fā)
              enterPictureInPictureMode();
              return;
          }
          ...
      }
    • 有意的離開您的應(yīng)用程序觸發(fā)(例如通過按下Home鍵)

      onUserLeaveHint()

      如要進入畫中畫模式,Activity必須調(diào)用enterPictureInPictureMode()。在用戶按下主屏幕或最近使用的應(yīng)用按鈕時,可以通過替換onUserLeaveHint()來實現(xiàn)應(yīng)用自動切換到畫中畫模式。

      @Override
      public void onUserLeaveHint () {
          // 有意的離開您的應(yīng)用程序觸發(fā)
          if (iWantToBeInPipModeNow()) {
              enterPictureInPictureMode();
          }
      }
    • 從返回觸發(fā)(例如通過按下返回按鈕)

      onBackPressed()

      @Override
      public void onBackPressed() {
          super.onBackPressed();
          // 從返回觸發(fā)
          enterPictureInPictureMode();
      }
  3. 處理畫中畫模式下的界面元素。

    當Activity進入或退出畫中畫模式時,系統(tǒng)會調(diào)用Activity.onPictureInPictureModeChanged()方法或Fragment.onPictureInPictureModeChanged()方法。

    您應(yīng)替換這些回調(diào)以重新繪制Activity的界面元素。請注意,在畫中畫模式下,Activity會以一個小窗口的形式顯示,用戶無法與應(yīng)用的界面元素進行互動,并且可能難以看清小窗口中的詳細信息。界面極簡的視頻播放Activity可提供最佳的用戶體驗。

    1. 退出畫中畫模式時支持更流暢的動畫。

      當Activity退出畫中畫模式時,您可以為界面元素添加退出動畫,以提升過渡的流暢性和視覺效果。

    2. 添加控件。

      在畫中畫模式下,用戶可能無法與應(yīng)用的界面元素進行互動。因此,您可以考慮在畫中畫窗口中添加一些簡單的控件,以便用戶可以進行基本的操作,如播放/暫停、上/下一集等。

    3. 為非視頻內(nèi)容停用無縫大小調(diào)整。

      在畫中畫模式下,視頻內(nèi)容通常需要保持適當?shù)拇笮”壤槐焕旎虿眉簟H欢瑢τ诜且曨l內(nèi)容,如文本、圖像等,可能需要禁用無縫大小調(diào)整,以避免內(nèi)容在小窗口中變得不可讀或失真。通過禁用無縫大小調(diào)整,可以確保非視頻內(nèi)容在畫中畫模式下保持良好的可見性和可操作性。

  4. 在畫中畫模式下繼續(xù)播放視頻。

    當Activity切換到畫中畫模式時,系統(tǒng)會將該Activity置于暫停狀態(tài)并調(diào)用Activity的onPause()方法。然而,在畫中畫模式下,視頻播放應(yīng)該繼續(xù)進行,而不是暫停。

    在Android 7.0及更高版本中

    • 當系統(tǒng)調(diào)用Activity的onStop()時,您應(yīng)暫停視頻播放。

    • 當系統(tǒng)調(diào)用Activity的onStart()時,您應(yīng)恢復(fù)視頻播放。

    這樣,您就無需在onPause()方法中檢查應(yīng)用是否處于畫中畫模式,只需繼續(xù)播放視頻即可。

    如果您必須在onPause()方法中暫停視頻播放,請通過調(diào)用isInPictureInPictureMode()方法來檢查是否處于畫中畫模式,并根據(jù)需要相應(yīng)地處理播放狀態(tài)。以下是一個示例代碼:

    @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.
            ...
        }
    }

iOS技術(shù)方案

目前有三種方式可以實現(xiàn)畫中畫功能:

  1. WKWebView自帶

    如果您在應(yīng)用中使用了WKWebView進行視頻播放,它已經(jīng)內(nèi)置了畫中畫功能。

  2. 使用AVPlayerViewController

    如果對播放器的要求不是很高,可以直接使用AVPlayerViewController。它已經(jīng)提供了畫中畫功能,只需設(shè)置allowsPictureInPicturePlayback屬性為YES,即可在播放器界面上展示畫中畫按鈕。

  3. 自定義播放器并使用AVPictureInPictureController包裝

    如果您使用自定義的播放器,并希望開啟畫中畫功能,可以使用AVPictureInPictureController對播放器進行包裝,簡單易用地實現(xiàn)畫中畫功能,并且AVPictureInPictureController內(nèi)部已經(jīng)實現(xiàn)了動畫效果。只需注意用戶需要自己實現(xiàn)畫中畫按鈕,系統(tǒng)已提供了相關(guān)API(pictureInPictureButtonStartImage)來使用畫中畫圖標。

懸浮窗

簡單理解為,可以利用UIWindow類型創(chuàng)建一個新的窗口,并將視頻播放器的視圖添加到該窗口上,通過手勢控制窗口的位置和大小,實現(xiàn)懸浮的畫中畫效果。

重要

在iOS的設(shè)計準則中,明確規(guī)定了不允許在應(yīng)用程序中使用懸浮窗。使用懸浮窗可能會被蘋果拒絕審核或被迫下架。同時也要考慮到用戶體驗和隱私保護問題,因此在使用懸浮窗時需要謹慎考慮。

畫中畫

畫中畫(Picture-in-Picture)功能在iOS 9版本就已經(jīng)推出,但在之前的版本中,該功能只能在iPad上使用。直到iOS 14版本,iPhone用戶才能開始使用畫中畫功能。

畫中畫功能在iOS上有兩種實現(xiàn)方案:

  1. 支持iOS 14以上版本的老方案。

    image

    在iOS 14系統(tǒng)中,通過使用系統(tǒng)提供的AVPlayer來初始化AVPictureInPictureController, 進而實現(xiàn)在應(yīng)用程序壓后臺或進入二級頁面時出現(xiàn)畫中畫效果。這種方案適用于對播放器要求不太高的場景。

  2. 支持iOS 15版本以上的新方案。

    image

    在iOS 15及更高版本中,可以使用SamplebufferLayer來創(chuàng)建AVPictureInPictureController,以實現(xiàn)無縫的畫中畫播放。這種方案通常用于自定義播放器來實現(xiàn)畫中畫功能。目前,阿里云播放器SDK已提供畫中畫功能,操作詳情請參見畫中畫

實現(xiàn)步驟如下:

iOS 15與iOS 14方案實現(xiàn)基本類似,本文以iOS 14老方案為例:

  1. 開啟后臺模式

    image

  2. 導(dǎo)入框架#import <AVKit/AVKit.h>創(chuàng)建AVPictureInPictureController。

    //1.判斷是否支持畫中畫功能
    if ([AVPictureInPictureController isPictureInPictureSupported]) {
        //2.開啟權(quán)限
        @try {
            NSError *error = nil;
            [[AVAudioSession sharedInstance] setCategory:AVAudioSessionOrientationBack error:&error];
            [[AVAudioSession sharedInstance] setActive:YES error:&error];
        } @catch (NSException *exception) {
            NSLog(@"AVAudioSession發(fā)生錯誤");
        }
        self.pipVC = [[AVPictureInPictureController alloc] initWithPlayerLayer:self.player];
        self.pipVC.delegate = self;
    }
  3. 開啟或關(guān)閉畫中畫。

    if (self.pipVC.isPictureInPictureActive) {
        [self.pipVC stopPictureInPicture];
    } else {
        [self.pipVC startPictureInPicture];
    }
  4. 代理AVPictureInPictureControllerDelegate。

    // 即將開啟畫中畫
    - (void)pictureInPictureControllerWillStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController;
    // 已經(jīng)開啟畫中畫
    - (void)pictureInPictureControllerDidStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController;
    // 開啟畫中畫失敗
    - (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController failedToStartPictureInPictureWithError:(NSError *)error;
    // 即將關(guān)閉畫中畫
    - (void)pictureInPictureControllerWillStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController;
    // 已經(jīng)關(guān)閉畫中畫
    - (void)pictureInPictureControllerDidStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController;
    // 關(guān)閉畫中畫且恢復(fù)播放界面
    - (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:(void (^)(BOOL restored))completionHandler;
    重要
    1. 通過一個全局變量持有畫中畫控制器,可以在pictureInPictureControllerWillStartPictureInPicture持有,在pictureInPictureControllerDidStopPictureInPicture釋放;

    2. 有時可能不是通過點擊畫中畫按鈕,而是通過其他途徑來打開當前的畫中畫控制器。可以在viewWillAppear方法中進行判斷并關(guān)閉。

    3. 在已經(jīng)存在畫中畫的情況下,若要開啟新的畫中畫,需等待完全關(guān)閉之后再進行新的開啟,以防止出現(xiàn)未知錯誤,因為關(guān)閉畫中畫是一個有過程的操作。

    4. 在創(chuàng)建AVPictureInPictureController并同時開啟畫中畫功能時,可能會出現(xiàn)失效的情況。如果遇到這種情況,建議延遲開啟畫中畫功能即可。