Phát video HTML5 trên toàn màn hình trong android webview


92

Chà, tôi đã tìm kiếm vài ngày rồi, làm thế nào để hiển thị video HTML5 ở chế độ toàn màn hình trên WebView android.

Tôi đã quản lý để phát video HTML5 trên webview của mình. Sự cố đang phát sinh khi hiển thị video ở chế độ toàn màn hình.

Như tôi đã tìm ra, android có hai cách xử lý thẻ <video>:

  1. Trên các phiên bản android <= 2.3.3 , phương thức onShowCustomView được kích hoạt và tôi có thể có phiên bản VideoView và thiết lập trình nghe khi video hoàn thành, đặt bộ điều khiển, v.v. Cho đến nay vẫn tốt.

  2. Trên ICS (và có thể là 3.0 trở lên) , có vẻ như <video> được xử lý theo cách khác. Khi video HTML5 được phát, onShowCustomView không được gọi ở chế độ bình thường - có vẻ như có một doanh nghiệp nội bộ bên trong WebView phát video và tất cả các điều khiển được xác định trong thẻ <video> được hiển thị - I không thể truy cập nó bằng bất kỳ cách nào. Trên thực tế, nếu video được phát ở chế độ bình thường, điều này là OK vì các nút điều khiển ở đó và đang hoạt động.

Điều đó dẫn tôi đến vấn đề lớn: khi hiển thị video ở chế độ toàn màn hình, onShowCustomView đang được gọi - nhưng trên ICS, tham số "view" không phải là một phiên bản của VideoView.

Tôi đã cố gắng phát hiện ra rằng trường hợp này là của VideoSurfaceView, một lớp bên trong riêng tư của lớp HTML5VideoFullScreen. Cách duy nhất chúng ta có thể truy cập lớp bên trong này là thông qua phản chiếu.

Sau khi xem xét GrepCode cho lớp này, tôi biết rằng không giống như VideoView, HTML5VideoFullScreen $ VideoSurfaceView không chứa một phiên bản MediaPlayer mà tôi có thể nghe các sự kiện của nó hoặc truy cập các điều khiển của nó. Điều duy nhất tôi có thể làm là sử dụng VideoSurfaceView này và đặt nó bên trong bố cục toàn màn hình mà không cần điều khiển.

Điểm mấu chốt - Khi hiển thị video toàn màn hình, tôi không biết khi nào video kết thúc, các nút điều khiển của nó không được hiển thị - điều này khá đáng buồn. Tôi không thể nhận được trình kích hoạt để đóng toàn màn hình.

Tôi đã thử một số cách giải quyết không thành công:

  1. Phản ánh: Tôi đã cố gắng tiếp cận phiên bản HTML5VideoFullScreen, chứa một thành viên MediaPlayer, từ VideoSurfaceView lớp bên trong. Tôi đã không quản lý để có được nó, tôi không chắc điều này có thể xảy ra (ViewSurfaceView không giữ bản sao của chủ sở hữu nó).

  2. Đăng ký các sự kiện video qua Javascript (ví dụ: onended) và xử lý những gì tôi cần trở lại trong JAVA thông qua JavascriptInterface: Tôi thấy giải pháp này không đáng tin cậy vì trong khi thực hiện việc này, tôi gặp phải một vấn đề khác: thẻ <video> có thể được lồng vào nhau trong một. Nguồn iframe không phải của tôi và tôi không thể lấy nội dung của nó (getElementById hoặc getElementsByTagName [i] là null) - có nghĩa là, tôi không thể truy cập phần tử <video> bên trong iframe.

Tôi vẫn đang tìm kiếm một giải pháp, rất ít bài viết về vấn đề này. Có ai quản lý để giải quyết nó? Trợ giúp sẽ được nhiều đánh giá cao!

Lớp VideoView : Tại đây (có MediaPlayer)

Lớp HTML5VideoFullScreen $ VideoSurfaceView : Tại đây (không có MediaPlayer)


Cùng một vấn đề như bạn, và cũng đi đến cùng một kết luận. Tôi đã làm việc này trong khoảng 20 giờ, nhưng sẽ không từ bỏ thêm ít nhất 20 giờ nữa (ý tôi là giờ làm việc thực tế). Sẽ cho bạn biết nếu tôi tìm thấy giải pháp. Trong khi đó, như câu hỏi này đã được hỏi cách đây 20 ngày, bạn vẫn đang cố gắng giải quyết nó hay bạn đã áp dụng bất kỳ cách giải quyết nào khác?
cprcrack

Nhân tiện, một cái nhìn nhanh về mã nguồn của HTML5VideoFullScreen cho thấy rằng HTML5VideoView lớp cha có chứa một MediaPlayer.
cprcrack

Câu trả lời:


176

Chỉnh sửa 2014/10: theo nhu cầu phổ biến, tôi đang duy trì và chuyển nó sang GitHub. Vui lòng kiểm tra cprcrack / VideoEnabledWebView để biết phiên bản mới nhất. Sẽ giữ câu trả lời này chỉ để tham khảo.

Chỉnh sửa 2014/01: cải thiện việc sử dụng ví dụ để bao gồm các chế độ xem nonVideoLayout, videoLayout và videoLoading, dành cho những người dùng yêu cầu thêm mã mẫu để hiểu rõ hơn.

Chỉnh sửa 2013/12: một số bản sửa lỗi liên quan đến khả năng tương thích của thiết bị Sony Xperia, nhưng thực tế đã ảnh hưởng đến tất cả các thiết bị.

Chỉnh sửa 2013/11: sau khi phát hành Android 4.4 KitKat (API cấp 19) với chế độ xem web Chromium mới, tôi đã phải làm việc chăm chỉ lại. Một số cải tiến đã được thực hiện. Bạn nên cập nhật phiên bản mới này. Tôi phát hành nguồn này dưới WTFPL .

Chỉnh sửa 2013/04: sau 1 tuần làm việc chăm chỉ, cuối cùng tôi đã đạt được mọi thứ mình cần. Tôi nghĩ rằng hai lớp chung này mà tôi đã tạo có thể giải quyết tất cả các vấn đề của bạn.

VideoEnabledWebChromeClientcó thể được sử dụng một mình nếu bạn không yêu cầu chức năng VideoEnabledWebViewbổ sung. Nhưng VideoEnabledWebViewphải luôn dựa vào a VideoEnabledWebChromeClient. Vui lòng đọc kỹ tất cả các bình luận của cả hai lớp.

Lớp 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;
        }
    }

}

Lớp 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;
        }
    }

}

Ví dụ sử dụng:

Bố cục chính activity_main.xml trong đó chúng tôi đặt VideoEnabledWebView và các chế độ xem đã sử dụng khác:

<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>

Activity của onCreate () , trong đó chúng tôi khởi tạo nó:

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");
}

Và đừng quên gọi 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
Những gì bạn đề nghị, sẽ thông báo khi video kết thúc chỉ trên API <= 10. Trên> = 11, không có gì đăng ký các onCompletionListener ...
nbtk

4
Cảm ơn những nỗ lực của bạn! Nhưng nếu <video> nằm dưới <iframe>, chúng tôi không thể tiếp cận nó, vì vậy phần javacsript không hoàn hảo. Mã của bạn rất chuyên nghiệp, ấn tượng.
nbtk

3
Việc kết hợp mã của câu trả lời này stackoverflow.com/questions/20379478/… sẽ giải quyết vấn đề tôi đặt ra ở trên.
Wienke Giezeman

2
Cần nâng cấp, không hoạt động trên android v4.4.4 +, vui lòng thêm tùy chọn vào onplay, tự động chuyển sang toàn màn hình. Bạn có thể triển khai vimeo trên mã này? Cảm ơn bạn đã chia sẻ mã trên github. @cprcrack
Florida

2
nó không hoạt động trong các phiên bản adroid mới nhất ... trên 5.0, 6.0,7.0
Umar-MOBITSOLUTIONS

12

Đã thử nghiệm trên phiên bản Android 9.0

Không có câu trả lời nào phù hợp với tôi. Đây là công việc cuối cùng

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);
    }
}

Trong AndroidManifest.xml

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

Nguồn Monster Techno


Thêm android: configChanges = "direction | screenSize" đã hoạt động hoàn hảo đối với tôi. Cảm ơn đã giúp đỡ!!!
Burak

Đặt WebChromeClient tùy chỉnh đã giải quyết được vấn đề cho tôi. Cảm ơn!
vato

5

Chỉnh sửa: vui lòng xem câu trả lời khác của tôi , vì có thể bạn không cần cái này bây giờ.

Như bạn đã nói, ở cấp API 11+, HTML5VideoFullScreen $ VideoSurfaceView sẽ được chuyển. Nhưng tôi không nghĩ bạn đúng khi nói rằng "nó không có MediaPlayer".

Đây là cách để truy cập cá thể MediaPlayer từ cá thể HTML5VideoFullScreen $ VideoSurfaceView bằng cách sử dụng phản chiếu :

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

Vì vậy, bây giờ bạn có thể đặt trình nghe onCompletion của phiên bản MediaPlayer như sau:

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);

Mã không bị lỗi nhưng tôi không hoàn toàn chắc chắn liệu trình nghe onCompletion đó có thực sự được gọi hay không hoặc nếu nó có thể hữu ích cho tình huống của bạn. Nhưng chỉ trong trường hợp ai đó muốn thử nó.


1
Cảm ơn sự giúp đỡ của bạn, tôi đã thử mã này - đầu tiên tôi có thể thấy rằng không phải tất cả các thiết bị đều sử dụng VideoSurfaceView bằng cách nào đó - HTC One X có VideoTextureView có lẽ là lớp tùy chỉnh của họ (không có trong tài liệu android). Tôi đã thử nó trên các thiết bị khác và trên các thiết bị khác, trình nghe hoàn thành đã được gọi.
nbtk

Tôi mở tùy chọn, tôi muốn phát video youtube trên cùng một hoạt động. Tôi làm gì?
asok Buzz,

Xin chào, tôi gặp ngoại lệ khi video chuyển sang chế độ toàn màn hình trên một số nhà phát triển stacktrace Đây là pastebin.com/9Gn9jmc2 Nếu tôi làm sai điều gì đó
Rajnish Mishra

@cprcrack có thể buộc thay đổi hướng hoạt động từ dọc sang ngang khi nhấp toàn màn hình vào video không?
Muhammad

1

Cảm ơn bạn rất nhiều vì lớp học đó, Cristian.

Tôi đã thực hiện một chỉnh sửa nhỏ đối với nó để chế độ xem tải tùy chỉnh là tùy chọn, như sau:

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

Tôi cũng đã thêm một hàm tạo mới chỉ nhận hai tham số. Dù sao, chỉ là một đơn giản hóa nhỏ nếu bạn không cần chế độ xem tải. Cảm ơn một lần nữa vì đã cung cấp điều này.


1

Chỉ cần đặt
mWebView.setWebChromeClient(new WebChromeClient());

và video phát như bình thường mà không cần bất kỳ chế độ xem tùy chỉnh nào.


7
Rất tiếc, không giúp được gì trong trường hợp của tôi
resource8218

0

Điều đó thật tuyệt. Nhưng nếu bạn muốn các liên kết trang web của mình mở trong chính ứng dụng, hãy thêm mã này vào ExampleActivity.java của bạn:

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

Câu trả lời của Cprcrack hoạt động rất tốt cho API cấp 19 trở xuống. Chỉ một bổ sung nhỏ cho cprcrack's onShowCustomViewsẽ làm cho nó hoạt động trên API cấp 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);
}

Bạn cũng sẽ cần phản ánh những thay đổi trong onHideCustomView


0

Có vẻ như trong lollipop trở lên (hoặc có thể chỉ là một Phiên bản WebView khác) mà cprcrack's onHideCustomView()phương thức gọi không hoạt động. Nó hoạt động nếu nó được gọi từ nút thoát toàn màn hình nhưng khi bạn gọi cụ thể phương thức, nó sẽ chỉ thoát khỏi chế độ toàn màn hình nhưng webViewvẫn trống. Một cách giải quyết là chỉ cần thêm các dòng mã này vào onHideCustomView():

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

Thao tác này sẽ thông báo cho webView rằng chế độ toàn màn hình đã thoát.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.