การเล่นวิดีโอ HTML5 แบบเต็มหน้าจอใน Android Webview


92

ฉันค้นหาไม่กี่วันแล้ววิธีแสดงวิดีโอ HTML5 ในโหมดเต็มหน้าจอบน Android WebView

ฉันจัดการเพื่อเล่นวิดีโอ HTML5 บน webview ของฉัน ปัญหาเกิดขึ้นเมื่อแสดงวิดีโอในโหมดเต็มหน้าจอ

ตามที่ฉันทราบแล้ว android มีสองวิธีในการจัดการแท็ก <video>:

  1. ในเวอร์ชัน Android <= 2.3.3เมธอด onShowCustomView จะเริ่มทำงานและฉันสามารถมีอินสแตนซ์ VideoView และตั้งค่าผู้ฟังเมื่อวิดีโอเล่นจบตั้งค่าคอนโทรลเลอร์ ฯลฯ จนถึงตอนนี้ดีมาก

  2. บน ICS (และอาจเป็น 3.0 ขึ้นไป)ดูเหมือนว่า <วิดีโอ> จะได้รับการจัดการในลักษณะที่แตกต่างกัน เมื่อเล่นวิดีโอ HTML5 onShowCustomView จะไม่ถูกเรียกในโหมดปกติดูเหมือนว่ามีธุรกิจภายในใน WebView ที่เล่นวิดีโอและการควบคุมทั้งหมดที่กำหนดไว้ในแท็ก <video> จะแสดงขึ้น - I ไม่สามารถเข้าถึงได้ แต่อย่างใด อันที่จริงหากเล่นวิดีโอในโหมดปกติก็ใช้ได้เพราะตัวควบคุมอยู่ที่นั่นและใช้งานได้

นั่นทำให้ฉันเจอปัญหาใหญ่เมื่อแสดงวิดีโอในโหมดเต็มหน้าจอจะมีการเรียก onShowCustomView - แต่ใน ICS พารามิเตอร์ "view" ไม่ใช่อินสแตนซ์ของ VideoView

ฉันจัดการเพื่อพบว่าอินสแตนซ์นั้นเป็นของ VideoSurfaceView ซึ่งเป็นคลาสภายในส่วนตัวของคลาส HTML5VideoFullScreen วิธีเดียวที่เราจะเข้าถึงชั้นในนี้ได้คือการสะท้อนกลับ

หลังจากดู GrepCode สำหรับคลาสนี้ฉันได้เรียนรู้ว่าแตกต่างจาก VideoView คือ HTML5VideoFullScreen $ VideoSurfaceView ไม่ได้มีอินสแตนซ์ MediaPlayer ที่ฉันสามารถฟังเหตุการณ์หรือเข้าถึงการควบคุมของมันได้ สิ่งเดียวที่ฉันทำได้คือใช้ VideoSurfaceView นี้เหมือนเดิมและวางไว้ในรูปแบบเต็มหน้าจอโดยไม่ต้องควบคุม

บรรทัดด้านล่าง - เมื่อแสดงวิดีโอแบบเต็มหน้าจอฉันไม่รู้ว่าเมื่อวิดีโอจบลงการควบคุมจะไม่แสดง - นี่เป็นเรื่องน่าเศร้า ฉันไม่สามารถเรียกใช้การปิดแบบเต็มหน้าจอได้

ฉันลองวิธีแก้ปัญหาที่ไม่ประสบความสำเร็จเพียงเล็กน้อย:

  1. การสะท้อนกลับ: ฉันพยายามเข้าถึงอินสแตนซ์ HTML5VideoFullScreen ซึ่งมีสมาชิก MediaPlayer จาก VideoSurfaceView ชั้นใน ฉันไม่สามารถจัดการได้ฉันไม่แน่ใจว่าเป็นไปได้ (ViewSurfaceView ไม่ถืออินสแตนซ์ของเจ้าของ)

  2. ลงทะเบียนสำหรับเหตุการณ์วิดีโอผ่าน Javascript (เช่น onended) และจัดการสิ่งที่ฉันต้องการใน JAVA ผ่าน JavascriptInterface: ฉันพบว่าโซลูชันนี้ไม่น่าเชื่อถือเพราะในขณะที่ทำสิ่งนี้ฉันพบปัญหาอื่น: แท็ก <video> สามารถซ้อน ใน. แหล่งที่มาของ iframe ไม่ใช่ของฉันและฉันไม่สามารถรับเนื้อหาได้ (getElementById หรือ getElementsByTagName [i] เป็นค่าว่าง) ซึ่งหมายความว่าฉันไม่สามารถเข้าถึงองค์ประกอบ <video> ภายใน iframe ได้

ฉันยังคงค้นหาวิธีแก้ปัญหามีการเขียนเกี่ยวกับปัญหานี้น้อยมาก มีใครจัดการแก้ไหม ความช่วยเหลือจะได้รับการชื่นชมมาก!

คลาสVideoView : ที่นี่ (มี MediaPlayer)

HTML5VideoFullScreen $ VideoSurfaceView class: ที่นี่ (ไม่มี MediaPlayer)


ปัญหาเดียวกับคุณและได้ข้อสรุปเช่นเดียวกัน ฉันทำงานนี้มาประมาณ 20 ชั่วโมงแล้ว แต่จะไม่ยอมแพ้อีกอย่างน้อย 20 ชั่วโมง (ฉันหมายถึงชั่วโมงการทำงานจริง) จะบอกคุณว่าฉันพบทางออก ในขณะเดียวกันเมื่อถามคำถามนี้เมื่อ 20 วันที่แล้วคุณยังคงพยายามแก้ไขอยู่หรือใช้วิธีแก้ปัญหาอื่น ๆ หรือไม่
cprcrack

อย่างไรก็ตามการดูซอร์สโค้ดของ HTML5VideoFullScreen อย่างรวดเร็วแสดงให้เห็นว่า HTML5VideoView ระดับแม่มี MediaPlayer
cprcrack

คำตอบ:


177

แก้ไข 2014/10: ตามความต้องการยอดนิยมฉันกำลังดูแลและย้ายสิ่งนี้ไปที่ GitHub โปรดตรวจสอบcprcrack / VideoEnabledWebViewสำหรับเวอร์ชันล่าสุด จะเก็บคำตอบนี้ไว้อ้างอิงเท่านั้น

แก้ไข 2014/01: ปรับปรุงการใช้งานตัวอย่างเพื่อรวมมุมมอง nonVideoLayout, videoLayout และ videoLoading สำหรับผู้ใช้เหล่านั้นที่ขอโค้ดตัวอย่างเพิ่มเติมเพื่อให้เข้าใจน้อยลง

แก้ไข 2013/12: การแก้ไขข้อบกพร่องบางอย่างที่เกี่ยวข้องกับความเข้ากันได้ของอุปกรณ์ Sony Xperia แต่ในความเป็นจริงส่งผลกระทบต่ออุปกรณ์ทั้งหมด

แก้ไข 2013/11: หลังจากการเปิดตัว Android 4.4 KitKat (API ระดับ 19) ด้วย Chromium webview ใหม่ฉันต้องทำงานหนักอีกครั้ง มีการปรับปรุงหลายอย่าง คุณควรอัปเดตเป็นเวอร์ชันใหม่นี้ ฉันปล่อยแหล่งนี้อยู่ภายใต้WTFPL

แก้ไข 2013/04: หลังจากทำงานหนักมา 1 สัปดาห์ในที่สุดฉันก็ทำทุกอย่างที่ต้องการได้สำเร็จ ฉันคิดว่าคลาสทั่วไปสองคลาสที่ฉันสร้างขึ้นสามารถแก้ปัญหาทั้งหมดของคุณได้

VideoEnabledWebChromeClientสามารถใช้คนเดียวได้หากคุณไม่ต้องการฟังก์ชันที่VideoEnabledWebViewเพิ่ม แต่VideoEnabledWebViewต้องอาศัยกVideoEnabledWebChromeClient. โปรดอ่านความคิดเห็นทั้งหมดของทั้งสองชั้นเรียนอย่างละเอียด

คลาส VideoEnabledWebChromeClient

import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.webkit.WebChromeClient;
import android.widget.FrameLayout;

/**
 * This class serves as a WebChromeClient to be set to a WebView, allowing it to play video.
 * Video will play differently depending on target API level (in-line, fullscreen, or both).
 *
 * It has been tested with the following video classes:
 * - android.widget.VideoView (typically API level <11)
 * - android.webkit.HTML5VideoFullScreen$VideoSurfaceView/VideoTextureView (typically API level 11-18)
 * - com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView (typically API level 19+)
 * 
 * Important notes:
 * - For API level 11+, android:hardwareAccelerated="true" must be set in the application manifest.
 * - The invoking activity must call VideoEnabledWebChromeClient's onBackPressed() inside of its own onBackPressed().
 * - Tested in Android API levels 8-19. Only tested on http://m.youtube.com.
 *
 * @author Cristian Perez (http://cpr.name)
 *
 */
public class VideoEnabledWebChromeClient extends WebChromeClient implements OnPreparedListener, OnCompletionListener, OnErrorListener
{
    public interface ToggledFullscreenCallback
    {
        public void toggledFullscreen(boolean fullscreen);
    }

    private View activityNonVideoView;
    private ViewGroup activityVideoView;
    private View loadingView;
    private VideoEnabledWebView webView;

    private boolean isVideoFullscreen; // Indicates if the video is being displayed using a custom view (typically full-screen)
    private FrameLayout videoViewContainer;
    private CustomViewCallback videoViewCallback;

    private ToggledFullscreenCallback toggledFullscreenCallback;

    /**
     * Never use this constructor alone.
     * This constructor allows this class to be defined as an inline inner class in which the user can override methods
     */
    @SuppressWarnings("unused")
    public VideoEnabledWebChromeClient()
    {
    }

    /**
     * Builds a video enabled WebChromeClient.
     * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
     * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
     */
    @SuppressWarnings("unused")
    public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView)
    {
        this.activityNonVideoView = activityNonVideoView;
        this.activityVideoView = activityVideoView;
        this.loadingView = null;
        this.webView = null;
        this.isVideoFullscreen = false;
    }

    /**
     * Builds a video enabled WebChromeClient.
     * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
     * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
     * @param loadingView A View to be shown while the video is loading (typically only used in API level <11). Must be already inflated and without a parent view.
     */
    @SuppressWarnings("unused")
    public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView, View loadingView)
    {
        this.activityNonVideoView = activityNonVideoView;
        this.activityVideoView = activityVideoView;
        this.loadingView = loadingView;
        this.webView = null;
        this.isVideoFullscreen = false;
    }

    /**
     * Builds a video enabled WebChromeClient.
     * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
     * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
     * @param loadingView A View to be shown while the video is loading (typically only used in API level <11). Must be already inflated and without a parent view.
     * @param webView The owner VideoEnabledWebView. Passing it will enable the VideoEnabledWebChromeClient to detect the HTML5 video ended event and exit full-screen.
     * Note: The web page must only contain one video tag in order for the HTML5 video ended event to work. This could be improved if needed (see Javascript code).
     */
    public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView, View loadingView, VideoEnabledWebView webView)
    {
        this.activityNonVideoView = activityNonVideoView;
        this.activityVideoView = activityVideoView;
        this.loadingView = loadingView;
        this.webView = webView;
        this.isVideoFullscreen = false;
    }

    /**
     * Indicates if the video is being displayed using a custom view (typically full-screen)
     * @return true it the video is being displayed using a custom view (typically full-screen)
     */
    public boolean isVideoFullscreen()
    {
        return isVideoFullscreen;
    }

    /**
     * Set a callback that will be fired when the video starts or finishes displaying using a custom view (typically full-screen)
     * @param callback A VideoEnabledWebChromeClient.ToggledFullscreenCallback callback
     */
    public void setOnToggledFullscreen(ToggledFullscreenCallback callback)
    {
        this.toggledFullscreenCallback = callback;
    }

    @Override
    public void onShowCustomView(View view, CustomViewCallback callback)
    {
        if (view instanceof FrameLayout)
        {
            // A video wants to be shown
            FrameLayout frameLayout = (FrameLayout) view;
            View focusedChild = frameLayout.getFocusedChild();

            // Save video related variables
            this.isVideoFullscreen = true;
            this.videoViewContainer = frameLayout;
            this.videoViewCallback = callback;

            // Hide the non-video view, add the video view, and show it
            activityNonVideoView.setVisibility(View.INVISIBLE);
            activityVideoView.addView(videoViewContainer, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
            activityVideoView.setVisibility(View.VISIBLE);

            if (focusedChild instanceof android.widget.VideoView)
            {
                // android.widget.VideoView (typically API level <11)
                android.widget.VideoView videoView = (android.widget.VideoView) focusedChild;

                // Handle all the required events
                videoView.setOnPreparedListener(this);
                videoView.setOnCompletionListener(this);
                videoView.setOnErrorListener(this);
            }
            else
            {
                // Other classes, including:
                // - android.webkit.HTML5VideoFullScreen$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 11-18)
                // - android.webkit.HTML5VideoFullScreen$VideoTextureView, which inherits from android.view.TextureView (typically API level 11-18)
                // - com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 19+)

                // Handle HTML5 video ended event only if the class is a SurfaceView
                // Test case: TextureView of Sony Xperia T API level 16 doesn't work fullscreen when loading the javascript below
                if (webView != null && webView.getSettings().getJavaScriptEnabled() && focusedChild instanceof SurfaceView)
                {
                    // Run javascript code that detects the video end and notifies the Javascript interface
                    String js = "javascript:";
                    js += "var _ytrp_html5_video_last;";
                    js += "var _ytrp_html5_video = document.getElementsByTagName('video')[0];";
                    js += "if (_ytrp_html5_video != undefined && _ytrp_html5_video != _ytrp_html5_video_last) {";
                    {
                        js += "_ytrp_html5_video_last = _ytrp_html5_video;";
                        js += "function _ytrp_html5_video_ended() {";
                        {
                            js += "_VideoEnabledWebView.notifyVideoEnd();"; // Must match Javascript interface name and method of VideoEnableWebView
                        }
                        js += "}";
                        js += "_ytrp_html5_video.addEventListener('ended', _ytrp_html5_video_ended);";
                    }
                    js += "}";
                    webView.loadUrl(js);
                }
            }

            // Notify full-screen change
            if (toggledFullscreenCallback != null)
            {
                toggledFullscreenCallback.toggledFullscreen(true);
            }
        }
    }

    @Override @SuppressWarnings("deprecation")
    public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) // Available in API level 14+, deprecated in API level 18+
    {
        onShowCustomView(view, callback);
    }

    @Override
    public void onHideCustomView()
    {
        // This method should be manually called on video end in all cases because it's not always called automatically.
        // This method must be manually called on back key press (from this class' onBackPressed() method).

        if (isVideoFullscreen)
        {
            // Hide the video view, remove it, and show the non-video view
            activityVideoView.setVisibility(View.INVISIBLE);
            activityVideoView.removeView(videoViewContainer);
            activityNonVideoView.setVisibility(View.VISIBLE);

            // Call back (only in API level <19, because in API level 19+ with chromium webview it crashes)
            if (videoViewCallback != null && !videoViewCallback.getClass().getName().contains(".chromium."))
            {
                videoViewCallback.onCustomViewHidden();
            }

            // Reset video related variables
            isVideoFullscreen = false;
            videoViewContainer = null;
            videoViewCallback = null;

            // Notify full-screen change
            if (toggledFullscreenCallback != null)
            {
                toggledFullscreenCallback.toggledFullscreen(false);
            }
        }
    }

    @Override
    public View getVideoLoadingProgressView() // Video will start loading
    {
        if (loadingView != null)
        {
            loadingView.setVisibility(View.VISIBLE);
            return loadingView;
        }
        else
        {
            return super.getVideoLoadingProgressView();
        }
    }

    @Override
    public void onPrepared(MediaPlayer mp) // Video will start playing, only called in the case of android.widget.VideoView (typically API level <11)
    {
        if (loadingView != null)
        {
            loadingView.setVisibility(View.GONE);
        }
    }

    @Override
    public void onCompletion(MediaPlayer mp) // Video finished playing, only called in the case of android.widget.VideoView (typically API level <11)
    {
        onHideCustomView();
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) // Error while playing video, only called in the case of android.widget.VideoView (typically API level <11)
    {
        return false; // By returning false, onCompletion() will be called
    }

    /**
     * Notifies the class that the back key has been pressed by the user.
     * This must be called from the Activity's onBackPressed(), and if it returns false, the activity itself should handle it. Otherwise don't do anything.
     * @return Returns true if the event was handled, and false if was not (video view is not visible)
     */
    public boolean onBackPressed()
    {
        if (isVideoFullscreen)
        {
            onHideCustomView();
            return true;
        }
        else
        {
            return false;
        }
    }

}

คลาส VideoEnabledWebView

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.webkit.WebChromeClient;
import android.webkit.WebView;

import java.util.Map;

/**
 * This class serves as a WebView to be used in conjunction with a VideoEnabledWebChromeClient.
 * It makes possible:
 * - To detect the HTML5 video ended event so that the VideoEnabledWebChromeClient can exit full-screen.
 * 
 * Important notes:
 * - Javascript is enabled by default and must not be disabled with getSettings().setJavaScriptEnabled(false).
 * - setWebChromeClient() must be called before any loadData(), loadDataWithBaseURL() or loadUrl() method.
 *
 * @author Cristian Perez (http://cpr.name)
 *
 */
public class VideoEnabledWebView extends WebView
{
    public class JavascriptInterface
    {
        @android.webkit.JavascriptInterface
        public void notifyVideoEnd() // Must match Javascript interface method of VideoEnabledWebChromeClient
        {
            // This code is not executed in the UI thread, so we must force that to happen
            new Handler(Looper.getMainLooper()).post(new Runnable()
            {
                @Override
                public void run()
                {
                    if (videoEnabledWebChromeClient != null)
                    {
                        videoEnabledWebChromeClient.onHideCustomView();
                    }
                }
            });
        }
    }

    private VideoEnabledWebChromeClient videoEnabledWebChromeClient;
    private boolean addedJavascriptInterface;

    public VideoEnabledWebView(Context context)
    {
        super(context);
        addedJavascriptInterface = false;
    }

    @SuppressWarnings("unused")
    public VideoEnabledWebView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        addedJavascriptInterface = false;
    }

    @SuppressWarnings("unused")
    public VideoEnabledWebView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        addedJavascriptInterface = false;
    }

    /**
     * Indicates if the video is being displayed using a custom view (typically full-screen)
     * @return true it the video is being displayed using a custom view (typically full-screen)
     */
    public boolean isVideoFullscreen()
    {
        return videoEnabledWebChromeClient != null && videoEnabledWebChromeClient.isVideoFullscreen();
    }

    /**
     * Pass only a VideoEnabledWebChromeClient instance.
     */
    @Override @SuppressLint("SetJavaScriptEnabled")
    public void setWebChromeClient(WebChromeClient client)
    {
        getSettings().setJavaScriptEnabled(true);

        if (client instanceof VideoEnabledWebChromeClient)
        {
            this.videoEnabledWebChromeClient = (VideoEnabledWebChromeClient) client;
        }

        super.setWebChromeClient(client);
    }

    @Override
    public void loadData(String data, String mimeType, String encoding)
    {
        addJavascriptInterface();
        super.loadData(data, mimeType, encoding);
    }

    @Override
    public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)
    {
        addJavascriptInterface();
        super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
    }

    @Override
    public void loadUrl(String url)
    {
        addJavascriptInterface();
        super.loadUrl(url);
    }

    @Override
    public void loadUrl(String url, Map<String, String> additionalHttpHeaders)
    {
        addJavascriptInterface();
        super.loadUrl(url, additionalHttpHeaders);
    }

    private void addJavascriptInterface()
    {
        if (!addedJavascriptInterface)
        {
            // Add javascript interface to be called when the video ends (must be done before page load)
            addJavascriptInterface(new JavascriptInterface(), "_VideoEnabledWebView"); // Must match Javascript interface name of VideoEnabledWebChromeClient

            addedJavascriptInterface = true;
        }
    }

}

ตัวอย่างการใช้งาน:

เค้าโครงหลักactivity_main.xmlซึ่งเราใส่ VideoEnabledWebView และมุมมองที่ใช้อื่น ๆ :

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <!-- View that will be hidden when video goes fullscreen -->
    <RelativeLayout
        android:id="@+id/nonVideoLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <your.package.VideoEnabledWebView
            android:id="@+id/webView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </RelativeLayout>   

    <!-- View where the video will be shown when video goes fullscreen -->
    <RelativeLayout
        android:id="@+id/videoLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <!-- View that will be shown while the fullscreen video loads (maybe include a spinner and a "Loading..." message) -->
        <View
            android:id="@+id/videoLoading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:visibility="invisible" />

    </RelativeLayout>

</RelativeLayout>

onCreate ()ของกิจกรรมซึ่งเราเริ่มต้น:

private VideoEnabledWebView webView;
private VideoEnabledWebChromeClient webChromeClient;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);

    // Set layout
    setContentView(R.layout.activity_main);

    // Save the web view
    webView = (VideoEnabledWebView) findViewById(R.id.webView);

    // Initialize the VideoEnabledWebChromeClient and set event handlers
    View nonVideoLayout = findViewById(R.id.nonVideoLayout); // Your own view, read class comments
    ViewGroup videoLayout = (ViewGroup) findViewById(R.id.videoLayout); // Your own view, read class comments
    View loadingView = getLayoutInflater().inflate(R.layout.view_loading_video, null); // Your own view, read class comments
    webChromeClient = new VideoEnabledWebChromeClient(nonVideoLayout, videoLayout, loadingView, webView) // See all available constructors...
    {
        // Subscribe to standard events, such as onProgressChanged()...
        @Override
        public void onProgressChanged(WebView view, int progress)
        {
            // Your code...
        }
    };
    webChromeClient.setOnToggledFullscreen(new VideoEnabledWebChromeClient.ToggledFullscreenCallback()
    {
        @Override
        public void toggledFullscreen(boolean fullscreen)
        {
            // Your code to handle the full-screen change, for example showing and hiding the title bar. Example:
            if (fullscreen)
            {
                WindowManager.LayoutParams attrs = getWindow().getAttributes();
                attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
                attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
                getWindow().setAttributes(attrs);
                if (android.os.Build.VERSION.SDK_INT >= 14)
                {
                    getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
                }
            }
            else
            {
                WindowManager.LayoutParams attrs = getWindow().getAttributes();
                attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
                attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
                getWindow().setAttributes(attrs);
                if (android.os.Build.VERSION.SDK_INT >= 14)
                {
                    getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
                }
            }

        }
    });
    webView.setWebChromeClient(webChromeClient);

    // Navigate everywhere you want, this classes have only been tested on YouTube's mobile site
    webView.loadUrl("http://m.youtube.com");
}

และอย่าลืมโทรหาonBackPressed () :

@Override
public void onBackPressed()
{
    // Notify the VideoEnabledWebChromeClient, and handle it ourselves if it doesn't handle it
    if (!webChromeClient.onBackPressed())
    {
        if (webView.canGoBack())
        {
            webView.goBack();
        }
        else
        {
            // Close app (presumably)
            super.onBackPressed();
        }
    }
}

1
สิ่งที่คุณแนะนำจะแจ้งเมื่อวิดีโอจบเฉพาะบน API <= 10 บน> = 11 ไม่มีอะไรลงทะเบียน onCompletionListener ...
nbtk

4
ขอบคุณสำหรับความพยายามของคุณ! แต่ถ้า <video> อยู่ภายใต้ <iframe> เราจะไม่สามารถเข้าถึงได้ดังนั้นส่วน javacsript จึงไม่สมบูรณ์แบบ รหัสของคุณเป็นมืออาชีพน่าประทับใจมาก
nbtk

3
การรวมรหัสของคำตอบนี้stackoverflow.com/questions/20379478/…จะช่วยแก้ปัญหาที่ฉันวางไว้ข้างต้น
Wienke Giezeman

3
ต้องการอัปเกรดไม่ทำงานบน Android v4.4.4 + โปรดเพิ่มตัวเลือกในการเล่นโดยอัตโนมัติไปที่แบบเต็มหน้าจอ คุณสามารถใช้ vimeo กับโค้ดนี้ได้หรือไม่? ขอบคุณสำหรับการแบ่งปันรหัสบน github @cprcrack
Florida

3
มันไม่ทำงานใน adroid เวอร์ชันล่าสุด ... สูงกว่า 5.0, 6.0,7.0
UMAR-MOBITSOLUTIONS

15

ทดสอบบนAndroidเวอร์ชัน9.0

ไม่มีคำตอบใดที่เหมาะกับฉัน นี่คือสิ่งสุดท้ายที่ใช้ได้ผล

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import android.widget.ProgressBar;

public class MainActivity extends AppCompatActivity {

    WebView mWebView;


    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        mWebView = (WebView) findViewById(R.id.webView);


        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new MyChrome());
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setAllowFileAccess(true);
        webSettings.setAppCacheEnabled(true);

       if (savedInstanceState == null) {
          mWebView.loadUrl("https://www.youtube.com/");
       }

    }


    private class MyChrome extends WebChromeClient {

        private View mCustomView;
        private WebChromeClient.CustomViewCallback mCustomViewCallback;
        protected FrameLayout mFullscreenContainer;
        private int mOriginalOrientation;
        private int mOriginalSystemUiVisibility;

        MyChrome() {}

        public Bitmap getDefaultVideoPoster()
        {
            if (mCustomView == null) {
                return null;
            }
            return BitmapFactory.decodeResource(getApplicationContext().getResources(), 2130837573);
        }

        public void onHideCustomView()
        {
            ((FrameLayout)getWindow().getDecorView()).removeView(this.mCustomView);
            this.mCustomView = null;
            getWindow().getDecorView().setSystemUiVisibility(this.mOriginalSystemUiVisibility);
            setRequestedOrientation(this.mOriginalOrientation);
            this.mCustomViewCallback.onCustomViewHidden();
            this.mCustomViewCallback = null;
        }

        public void onShowCustomView(View paramView, WebChromeClient.CustomViewCallback paramCustomViewCallback)
        {
            if (this.mCustomView != null)
            {
                onHideCustomView();
                return;
            }
            this.mCustomView = paramView;
            this.mOriginalSystemUiVisibility = getWindow().getDecorView().getSystemUiVisibility();
            this.mOriginalOrientation = getRequestedOrientation();
            this.mCustomViewCallback = paramCustomViewCallback;
            ((FrameLayout)getWindow().getDecorView()).addView(this.mCustomView, new FrameLayout.LayoutParams(-1, -1));
            getWindow().getDecorView().setSystemUiVisibility(3846 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        }
    }

 @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mWebView.saveState(outState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        mWebView.restoreState(savedInstanceState);
    }
}

ในAndroidManifest.xml

<activity
  android:name=".MainActivity"
  android:configChanges="orientation|screenSize" />

ที่มาMonster Techno


การเพิ่มandroid: configChanges = "orientation | screenSize"ทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน ขอบคุณสำหรับความช่วยเหลือ !!!
Burak

การตั้งค่า WebChromeClient แบบกำหนดเองช่วยแก้ปัญหาให้ฉันได้ ขอบคุณ!
vato

ขอบคุณ! ช่วยให้ฉันเรียกใช้UniteGalleryใน Android WebView
Jan-Peter Schmidt

5

แก้ไข: โปรดดูคำตอบอื่นของฉันเนื่องจากคุณอาจไม่ต้องการสิ่งนี้ในตอนนี้

ดังที่คุณกล่าวว่าในระดับ API 11+ จะมีการส่ง HTML5VideoFullScreen $ VideoSurfaceView แต่ฉันไม่คิดว่าคุณพูดถูกเมื่อคุณพูดว่า "มันไม่มี MediaPlayer"

นี่เป็นวิธีเข้าถึงอินสแตนซ์ MediaPlayer จากอินสแตนซ์ HTML5VideoFullScreen $ VideoSurfaceView โดยใช้การสะท้อน :

@SuppressWarnings("rawtypes")
Class c1 = Class.forName("android.webkit.HTML5VideoFullScreen$VideoSurfaceView");
Field f1 = c1.getDeclaredField("this$0");
f1.setAccessible(true);

@SuppressWarnings("rawtypes")
Class c2 = f1.getType().getSuperclass();
Field f2 = c2.getDeclaredField("mPlayer");
f2.setAccessible(true);

Object ___html5VideoViewInstance = f1.get(focusedChild); // Look at the code in my other answer to this same question to see whats focusedChild

Object ___mpInstance = f2.get(___html5VideoViewInstance); // This is the MediaPlayer instance.

ตอนนี้คุณสามารถตั้งค่าตัวฟัง onCompletion ของอินสแตนซ์ MediaPlayer ได้ดังนี้:

OnCompletionListener ocl = new OnCompletionListener()
{
    @Override
    public void onCompletion(MediaPlayer mp)
    {
        // Do stuff
    }
};

Method m1 = f2.getType().getMethod("setOnCompletionListener", new Class[] { Class.forName("android.media.MediaPlayer$OnCompletionListener") });
m1.invoke(___mpInstance, ocl);

รหัสไม่ล้มเหลว แต่ฉันไม่แน่ใจอย่างสมบูรณ์ว่าจะมีการเรียกตัวฟัง onCompletion นั้นจริงๆหรืออาจเป็นประโยชน์กับสถานการณ์ของคุณ แต่เผื่อว่าจะมีคนอยากลอง


1
ขอบคุณสำหรับความช่วยเหลือของคุณฉันลองใช้รหัสนี้ก่อนอื่นฉันจะเห็นว่าอุปกรณ์บางอย่างไม่ได้ใช้ VideoSurfaceView อย่างใด - HTC One X มี VideoTextureView ซึ่งอาจเป็นคลาสที่กำหนดเอง (ไม่ใช่ในเอกสาร Android) ฉันได้ลองใช้กับอุปกรณ์อื่น ๆ และในอุปกรณ์ที่ไม่ใช่อุปกรณ์นั้นจะมีการเรียกใช้ตัวฟังที่สมบูรณ์
nbtk

ฉันเปิดรับตัวเลือกฉันต้องการเล่นวิดีโอ youtube ในกิจกรรมเดียวกัน ฉันจะทำอย่างไร?
asok Buzz

สวัสดีฉันได้รับข้อยกเว้นเมื่อวิดีโอแสดงเต็มหน้าจอในบางส่วนที่เบี่ยงเบน stacktrace อยู่ที่นี่pastebin.com/9Gn9jmc2หากฉันทำอะไรผิดพลาด
Rajnish Mishra

@cprcrack เป็นไปได้หรือไม่ที่จะบังคับให้เปลี่ยนการวางแนวกิจกรรมจากแนวตั้งเป็นแนวนอนเมื่อคลิกเต็มหน้าจอบนวิดีโอ
มูฮัมหมัด

1

ขอบคุณมากสำหรับชั้นเรียนนั้น Cristian

ฉันได้ทำการปรับแต่งเล็กน้อยเพื่อให้มุมมองการโหลดแบบกำหนดเองเป็นทางเลือกดังนี้:

  @Override
    public View getVideoLoadingProgressView() // Video will start loading, only called in the case of VideoView (typically API level 10-)
    {
        if (loadingView == null)
        {
            return super.getVideoLoadingProgressView();
        }
        else
        {
            loadingView.setVisibility(View.VISIBLE);
            return loadingView;
        }
    }

ฉันยังเพิ่มตัวสร้างใหม่ที่ใช้เวลาเพียงสองพารามิเตอร์ อย่างไรก็ตามการทำให้เข้าใจง่ายขึ้นเล็กน้อยหากคุณไม่ต้องการมุมมองการโหลด ขอขอบคุณอีกครั้งที่ให้สิ่งนี้


1

เพียงแค่ตั้งค่า
mWebView.setWebChromeClient(new WebChromeClient());

และวิดีโอเล่นได้ตามปกติโดยไม่จำเป็นต้องมีมุมมองที่กำหนดเอง


8
น่าเสียดายที่ไม่ได้ช่วยในกรณีของฉัน
resource8218

0

นี่มันเยี่ยมมาก แต่ถ้าคุณต้องการให้ลิงก์เว็บไซต์ของคุณเปิดในแอปเองให้เพิ่มโค้ดนี้ใน ExampleActivity.java ของคุณ:

webView.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if (Uri.parse(url).getHost().endsWith("yourwebsite.com")) {
                return false;
            }

            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            view.getContext().startActivity(intent);
            return true;
        }
    });

0

คำตอบของ Cprcrack ทำงานได้ดีสำหรับ API ระดับ 19 และต่ำกว่า การเพิ่ม cprcrack เพียงเล็กน้อยก็onShowCustomViewจะทำให้ใช้งานได้บน API ระดับ 21+

if (Build.VERSION.SDK_INT >= 21) {
      videoViewContainer.setBackgroundColor(Color.BLACK);
      ((ViewGroup) webView.getParent()).addView(videoViewContainer);
      webView.scrollTo(0,0);  // centers full screen view 
} else {
      activityNonVideoView.setVisibility(View.INVISIBLE);
      ViewGroup.LayoutParams vg = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT);
      activityVideoView.addView(videoViewContainer,vg);
      activityVideoView.setVisibility(View.VISIBLE);
}

คุณจะต้องสะท้อนถึงการเปลี่ยนแปลงใน onHideCustomView


0

ดูเหมือนว่าใน lollipop ขึ้นไป (หรืออาจเป็นแค่ WebView Version อื่น) cprcrack's onHideCustomView()วิธีการโทรนั้นใช้ไม่ได้ มันทำงานได้ถ้ามันถูกเรียกจากปุ่มออกจากเต็มหน้าจอ แต่เมื่อคุณโดยเฉพาะเรียกวิธีก็จะออกจากเต็มหน้าจอ แต่webViewการเข้าพักที่ว่างเปล่า วิธีแก้ปัญหาคือเพียงเพิ่มบรรทัดของโค้ดเหล่านี้ในonHideCustomView():

String js = "javascript:";
js += "var _ytrp_html5_video = document.getElementsByTagName('video')[0];";
js += "_ytrp_html5_video.webkitExitFullscreen();";
webView.loadUrl(js);

สิ่งนี้จะแจ้งให้ webView ทราบว่าออกจากโหมดเต็มหน้าจอแล้ว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.