Android自定义WebView实现Youtube网络视频播放控件

原创 2016年08月01日 11:55:58


实现效果

项目github地址:https://github.com/AlexZhuo/AlxYoutubePlayer

因为业务需要,以下代码均以Youtube网站在线视频为例

实现功能:

1、初始化的时候显示标题和视频封面

2、初始化的时候显示一个play按钮

3、不需要依赖任何SDK,或者导入任何第三方库

4、播放过程中可以暂停,可以拖动进度条快进

5、可以全屏播放

6、切换页面的时候会自动暂停

7、页面退出的时候自动销毁WebView

8、不需要申请任何开发者账号或者获取授权

原理:

首先需要一个继承WebView的自定义控件,这里起名叫做YoutubePlayerView,在页面初始化的时候用这个WebView去加载一个事先写好的HTML,当然在加载之前,需要把Youtube的视频id和一些播放参数设置进去。然后一个小的播放窗口就完成了,此时已经完成用户点击play按钮就播放的功能。

但是光能播放还不行,我们还需要捕捉用户的点击事件,比如播放,暂停等等操作,而这些操作本身写在Youtube的JS代码中(Youtube已经把JS调用相关代码的位置预留好,就等着开发者来复写相关的代码了),需要在JS代码中调用java代码,这样就需要有一个JS调用java的接口,这里起名叫QualsonBridge,通过使用WebVIew的addJavascriptInterface()方法将Java代码的接口设置进去,并且需要一个接口实现类,实现的方法名称方法要和JS接口规定的方法一模一样,以便反射调用,一会会把详细的代码贴出来。

完成以上两点,就已经完成了播放,暂停等操作,但是还需要在Activity退出或者被覆盖的时候暂停WebView的播放,所以还需要给这个WebView写一个onDestroy的方法,并在fragment的onDestroy中调用,里面执行的主要就是清楚缓存的操作,还需要WebView写一个onPause的方法,在fragment的onPause中调用,里面主要执行JS代码:javascript:onVideoPause()

关于全屏播放:是通过一个自定义的WebChromeClient来实现将WebView扩大到全屏并修改旋转角度进行播放


代码实现:

首先需要让WebView去加载一块HTML,这段HTML是从Youtube的官方SDK中抽取出来的,本质上是一个HTML编写的小播放窗口,代码如下

<!DOCTYPE html>
<html>
<style type="text/css">
  html, body {
     height:100%;
     width:100%;
     margin: 0;
     padding: 0;
     background:[BG_COLOR];
     overflow:hidden;
     position:relative;
  }
</style>
<script type = "text/javascript" src = "http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script type = "text/javascript" src = "https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/jquery-ui.min.js"></script>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
<script src="https://www.youtube.com/iframe_api"></script>
</head>
<body>
  <div id="QPlayer"></div>
</body>
<script type="text/javascript">
  var player;
  function onYouTubeIframeAPIReady() {
      player = new YT.Player('QPlayer', {
      height: '100%',
      width: '100%',
      videoId: '[VIDEO_ID]',
      events: {
        'onReady': onPlayerReady,
        'onStateChange': onPlayerStateChange,
        'onPlaybackQualityChange': onPlayerPlaybackQualityChange,
        'onPlaybackRateChange': onPlayerPlaybackRateChange,
        'onError': onPlayerError,
        'onApiChange': onPlayerApiChange
      },
      playerVars: {
        'autoplay': [AUTO_PLAY],
        'autohide':[AUTO_HIDE],
        'rel': [REL],
        'showinfo': [SHOW_INFO],
        'enablejsapi': [ENABLE_JS_API],
        'disablekb': [DISABLE_KB],
        'cc_lang_pref': '[CC_LANG_PREF]',
        'controls': [CONTROLS],
		'fs' : [FS],
		'origin' : 'https://www.youtube.com'
      }
    });
  }

  function onPlayerReady(event) {
    console.log('player is ready');
    onReady('player is ready');
    sendDuration();
    player.setOption("captions", "track", {"languageCode": "es"});
    player.unloadModule("captions");
  }

  var timerId = 0;
  function onPlayerStateChange(event) {
    clearTimeout(timerId);
    switch (event.data) {
      case YT.PlayerState.UNSTARTED:
        onStateChange("UNSTARTED");
        break;
      case YT.PlayerState.ENDED:
        onStateChange("ENDED");
        break;
      case YT.PlayerState.PLAYING:
        player.unloadModule("captions");
        onStateChange("PLAYING");
        timerId = setInterval(function() {
          setCurrentSeconds();
        }, 100);
        break;
      case YT.PlayerState.PAUSED:
        onStateChange("PAUSED");
        break;
      case YT.PlayerState.BUFFERING:
        onStateChange("BUFFERING");
        break;
      case YT.PlayerState.CUED:
        onStateChange("CUED");
        break;
    }
  }
//底下的这些function就是调用java代码的接口函数
  function onPlayerPlaybackQualityChange(playbackQuality) {
   console.log('playback quality changed to ' + playbackQuality.data);
   onPlaybackQualityChange('playback quality changed to ' + playbackQuality.data);
  }

  function onPlayerPlaybackRateChange(playbackRate) {
   console.log('playback rate changed to ' + playbackRate.data);
   onPlaybackRateChange('playback rate changed to ' + playbackRate.data);
  }

  function onPlayerError(e) {
   console.log('An error occurred: ' + e.data);
   onError('An error occurred: ' + e.data);
  }

  function onPlayerApiChange() {
   console.log('The player API changed');
   onApiChange('The player API changed');
  }

  function onReady(e){
    window.QualsonInterface.onReady(e);
  }
//这个函数是最重要的,用于通知Android代码播放状态改变
  function onStateChange(e){
    window.QualsonInterface.onStateChange(e);
  }

  function onPlaybackQualityChange(e){
    window.QualsonInterface.onPlaybackQualityChange(e);
  }

  function onPlaybackRateChange(e){
    window.QualsonInterface.onPlaybackRateChange(e);
  }

  function onError(e){
    window.QualsonInterface.onError(e);
  }

  function onApiChange(e){
    window.QualsonInterface.onApiChange(e);
  }

  function setCurrentSeconds(){
    window.QualsonInterface.currentSeconds(player.getCurrentTime());
  }

  function sendDuration(){
    window.QualsonInterface.duration(player.getDuration());
  }

  function setLog(msg){
    window.QualsonInterface.logs(msg);
  }

  function onSeekTo(startSeconds){
    player.seekTo(startSeconds, true)
  }

  function onVideoPause(){
    player.pauseVideo();
  }

  function onVideoStop(){
    player.stopVideo();
  }

  function onVideoPlay(){
    player.playVideo();
  }

  function onHideControls(){
    setLog("onHideControls()");
  }

  function loadVideo(videoId, startSeconds){
    setLog(videoId + "_" + startSeconds);
    player.loadVideoById(videoId, startSeconds);
  }

  function cueVideo(videoId){
    setLog(videoId);
    player.cueVideoById(videoId, 0, "default");
    player.setVolume(100)
  }
</script>
</html>

项目中把这一段代码放到了raw文件夹下,并通过下面这样一个方法去加载,并在加载的同时,把上面预留的一些参数比如视频id什么的给补上

/**
     * 自己写一段HTML,并设置好Youtube的视频id,放到WebView中进行显示
     * @param videoId
     * @return
     */
    private String getVideoHTML(String videoId) {
        try {
            InputStream in = getResources().openRawResource(R.raw.players);
            if (in != null) {
                InputStreamReader stream = new InputStreamReader(in, "utf-8");
                BufferedReader buffer = new BufferedReader(stream);
                String read;
                StringBuilder sb = new StringBuilder("");

                while ((read = buffer.readLine()) != null) {
                    sb.append(read + "\n");
                }

                in.close();

                String html = sb.toString().replace("[VIDEO_ID]", videoId).replace("[BG_COLOR]", backgroundColor);
                html = html.replace("[AUTO_PLAY]", String.valueOf(params.getAutoplay())).replace("[AUTO_HIDE]", String.valueOf(params.getAutohide())).replace("[REL]", String.valueOf(params.getRel())).replace("[SHOW_INFO]", String.valueOf(params.getShowinfo())).replace("[ENABLE_JS_API]", String.valueOf(params.getEnablejsapi())).replace("[DISABLE_KB]", String.valueOf(params.getDisablekb())).replace("[CC_LANG_PREF]", String.valueOf(params.getCc_lang_pref())).replace("[CONTROLS]", String.valueOf(params.getControls())).replace("[FS]", String.valueOf(params.getFs()));
                return html;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
这里面传来的videoId一般是从Youtube视频分享url中用正则解析出来的,比如:https://youtu.be/DdRwiH4mR0Q

DdRwiH4mR0Q就是videoId

还需要一个给JS代码调用java代码的接口,要复写上面html中events中和function中的所有方法,如下

/**
     * WEB TO APP Javascript的安卓接口,用于在安卓上部署JS代码,这里是将JS回调到Android中,让JS触发Java代码
     * 需要在JS代码合适地方调用这里面的方法,在js中有一个函数如下:
     * function onPlayerStateChange(event)
     * 和这样一个函数
     * function onStateChange(e){
            window.QualsonInterface.onStateChange(e);//用于回调java代码
        }
     并且这个需要在java代码中使用 this.addJavascriptInterface(bridge, "QualsonInterface");来注册
     */
    private class QualsonBridge {

        @JavascriptInterface
        public void onReady(String arg) {
            JLogUtils.d(TAG, "onReady(" + arg + ")");
            if (youTubeListener != null) {
                youTubeListener.onReady();
            }
        }

        @JavascriptInterface
        public void onStateChange(String arg) {
            JLogUtils.d(TAG, "onStateChange(" + arg + ")");
            if ("UNSTARTED".equalsIgnoreCase(arg)) {
                notifyStateChange(STATE.UNSTARTED);
            } else if ("ENDED".equalsIgnoreCase(arg)) {
                notifyStateChange(STATE.ENDED);
            } else if ("PLAYING".equalsIgnoreCase(arg)) {
                notifyStateChange(STATE.PLAYING);
            } else if ("PAUSED".equalsIgnoreCase(arg)) {
                notifyStateChange(STATE.PAUSED);
            } else if ("BUFFERING".equalsIgnoreCase(arg)) {
                notifyStateChange(STATE.BUFFERING);
            } else if ("CUED".equalsIgnoreCase(arg)) {
                notifyStateChange(STATE.CUED);
            }
        }

        @JavascriptInterface
        public void onPlaybackQualityChange(String arg) {
            JLogUtils.d(TAG, "onPlaybackQualityChange(" + arg + ")");
            if (youTubeListener != null) {
                youTubeListener.onPlaybackQualityChange(arg);
            }
        }

        @JavascriptInterface
        public void onPlaybackRateChange(String arg) {
            JLogUtils.d(TAG, "onPlaybackRateChange(" + arg + ")");
            if (youTubeListener != null) {
                youTubeListener.onPlaybackRateChange(arg);
            }
        }

        @JavascriptInterface
        public void onError(String arg) {
            JLogUtils.e(TAG, "onError(" + arg + ")");
            if (youTubeListener != null) {
                youTubeListener.onError(arg);
            }
        }

        @JavascriptInterface
        public void onApiChange(String arg) {
            JLogUtils.d(TAG, "onApiChange(" + arg + ")");
            if (youTubeListener != null) {
                youTubeListener.onApiChange(arg);
            }
        }

        @JavascriptInterface
        public void currentSeconds(String seconds) {
            if (youTubeListener != null) {
                youTubeListener.onCurrentSecond(Double.parseDouble(seconds));
            }
        }

        @JavascriptInterface
        public void duration(String seconds) {
            if (youTubeListener != null) {
                youTubeListener.onDuration(Double.parseDouble(seconds));
            }
        }

        @JavascriptInterface
        public void logs(String arg) {
            JLogUtils.d(TAG, "logs(" + arg + ")");
            if (youTubeListener != null) {
                youTubeListener.logs(arg);
            }
        }
    }

向js注册这个JAVA接口使用WebView的addJavascriptInterface(bridge, "QualsonInterface");方法

这里面的youTubeListener是自己写的一个接口类,用于方便回调UI方法的

下面贴出这个自定义WebView的完整代码:

package com.imaginato.qravedconsumer.widget.player;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.qraved.app.R;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;

public class YoutubePlayerView extends WebView {

    private static final String TAG = YoutubePlayerView.class.getSimpleName();

    private QualsonBridge bridge = new QualsonBridge();

    private YTParams params = new YTParams();

    private YouTubeListener youTubeListener;
    private String backgroundColor = "#000000";
    private STATE mPlayState = STATE.UNSTARTED;

    public YoutubePlayerView(Context context) {
        super(context);
        setWebViewClient(new MyWebViewClient((Activity) context));
    }

    public YoutubePlayerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setWebViewClient(new MyWebViewClient((Activity) context));
    }

    @SuppressLint("JavascriptInterface")
    public void initialize(String videoId, YouTubeListener youTubeListener, WebChromeClient webChromeClient) {
        WebSettings set = this.getSettings();
        set.setJavaScriptEnabled(true);
        set.setUseWideViewPort(true);
        set.setLoadWithOverviewMode(true);
        set.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
        set.setCacheMode(WebSettings.LOAD_NO_CACHE);
        set.setPluginState(WebSettings.PluginState.ON);
        set.setPluginState(WebSettings.PluginState.ON_DEMAND);
        set.setAllowContentAccess(true);
        set.setAllowFileAccess(true);

        if (webChromeClient != null) {
            this.setWebChromeClient(webChromeClient);
        }

        this.mPlayState = STATE.UNSTARTED;
        this.youTubeListener = youTubeListener;
        this.setLayerType(View.LAYER_TYPE_NONE, null);
        this.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
        this.addJavascriptInterface(bridge, "QualsonInterface");//注册js代码调用java代码的接口
        this.loadDataWithBaseURL("https://www.youtube.com", getVideoHTML(videoId), "text/html", "utf-8", null);
        this.setLongClickable(true);
        this.setOnLongClickListener(new OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                return true;
            }
        });

    }

    public void initialize(String videoId, YTParams params, YouTubeListener youTubeListener, WebChromeClient webChromeClient) {
        if (params != null) {
            this.params = params;
        }
        initialize(videoId, youTubeListener, webChromeClient);
    }

    public void setWhiteBackgroundColor() {
        backgroundColor = "#ffffff";
    }

    public void setAutoPlayerHeight(Context context) {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        this.getLayoutParams().height = (int) (displayMetrics.widthPixels * 0.5625);
    }

    /**
     * 让WebView去执行JS代码javascript:onVideoPause(),来暂停视频
     */
    public void pause() {
        Log.d(TAG, "pause");
        this.loadUrl("javascript:onVideoPause()");
    }
    /**
     * 让WebView去执行JS代码,来停止视频
     */
    public void stop(){
        Log.d(TAG,"stop");
        this.loadUrl("javascript:onVideoStop()");
    }

    public STATE getPlayerState(){
        Log.d(TAG,"getPlayerState");
        return mPlayState;
    }

    public void play() {
        Log.d(TAG, "play");
        this.loadUrl("javascript:onVideoPlay()");
    }

    private void notifyStateChange(STATE state){
        if(youTubeListener!=null){
            youTubeListener.onStateChange(state);
        }
        this.mPlayState = state;
    }

    /**
     * WEB TO APP Javascript的安卓接口,用于在安卓上部署JS代码,这里是将JS回调到Android中,让JS触发Java代码
     * 需要在JS代码合适地方调用这里面的方法,在js中有一个函数如下:
     * function onPlayerStateChange(event)
     * 和这样一个函数
     * function onStateChange(e){
            window.QualsonInterface.onStateChange(e);//用于回调java代码
        }
     并且这个需要在java代码中使用 this.addJavascriptInterface(bridge, "QualsonInterface");来注册
     */
    private class QualsonBridge {

        @JavascriptInterface
        public void onReady(String arg) {
            Log.d(TAG, "onReady(" + arg + ")");
            if (youTubeListener != null) {
                youTubeListener.onReady();
            }
        }

        @JavascriptInterface
        public void onStateChange(String arg) {
            Log.d(TAG, "onStateChange(" + arg + ")");
            if ("UNSTARTED".equalsIgnoreCase(arg)) {
                notifyStateChange(STATE.UNSTARTED);
            } else if ("ENDED".equalsIgnoreCase(arg)) {
                notifyStateChange(STATE.ENDED);
            } else if ("PLAYING".equalsIgnoreCase(arg)) {
                notifyStateChange(STATE.PLAYING);
            } else if ("PAUSED".equalsIgnoreCase(arg)) {
                notifyStateChange(STATE.PAUSED);
            } else if ("BUFFERING".equalsIgnoreCase(arg)) {
                notifyStateChange(STATE.BUFFERING);
            } else if ("CUED".equalsIgnoreCase(arg)) {
                notifyStateChange(STATE.CUED);
            }
        }

        @JavascriptInterface
        public void onPlaybackQualityChange(String arg) {
            Log.d(TAG, "onPlaybackQualityChange(" + arg + ")");
            if (youTubeListener != null) {
                youTubeListener.onPlaybackQualityChange(arg);
            }
        }

        @JavascriptInterface
        public void onPlaybackRateChange(String arg) {
            Log.d(TAG, "onPlaybackRateChange(" + arg + ")");
            if (youTubeListener != null) {
                youTubeListener.onPlaybackRateChange(arg);
            }
        }

        @JavascriptInterface
        public void onError(String arg) {
            Log.e(TAG, "onError(" + arg + ")");
            if (youTubeListener != null) {
                youTubeListener.onError(arg);
            }
        }

        @JavascriptInterface
        public void onApiChange(String arg) {
            Log.d(TAG, "onApiChange(" + arg + ")");
            if (youTubeListener != null) {
                youTubeListener.onApiChange(arg);
            }
        }

        @JavascriptInterface
        public void currentSeconds(String seconds) {
            if (youTubeListener != null) {
                youTubeListener.onCurrentSecond(Double.parseDouble(seconds));
            }
        }

        @JavascriptInterface
        public void duration(String seconds) {
            if (youTubeListener != null) {
                youTubeListener.onDuration(Double.parseDouble(seconds));
            }
        }

        @JavascriptInterface
        public void logs(String arg) {
            Log.d(TAG, "logs(" + arg + ")");
            if (youTubeListener != null) {
                youTubeListener.logs(arg);
            }
        }
    }


    /**
     * NonLeakingWebView
     */
    private static Field sConfigCallback;

    static {
        try {
            sConfigCallback = Class.forName("android.webkit.BrowserFrame").getDeclaredField("sConfigCallback");
            sConfigCallback.setAccessible(true);
        } catch (Exception e) {
            // ignored
        }
    }

    public void onDestroy() {
        super.onDetachedFromWindow();
        // View is now detached, and about to be destroyed
        youTubeListener = null;
        this.clearCache(true);
        this.clearHistory();
        try {
            if (sConfigCallback != null)
                sConfigCallback.set(null, null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private class MyWebViewClient extends WebViewClient {
        protected WeakReference<Activity> activityRef;

        public MyWebViewClient(Activity activity) {
            this.activityRef = new WeakReference<Activity>(activity);
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            try {
                final Activity activity = activityRef.get();
                if (activity != null)
                    activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
            } catch (RuntimeException ignored) {
                // ignore any url parsing exceptions
            }
            return true;
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            Log.d(TAG, "onPageFinished()");
        }
    }

    public interface YouTubeListener {
        void onReady();//可以显示播放按钮进行播放

        void onStateChange(STATE state);//暂停等等状态

        void onPlaybackQualityChange(String arg);//清晰度改变

        void onPlaybackRateChange(String arg);

        void onError(String arg);

        void onApiChange(String arg);

        void onCurrentSecond(double second);

        void onDuration(double duration);

        void logs(String log);
    }

    public enum STATE {
        UNSTARTED,
        ENDED,
        PLAYING,
        PAUSED,
        BUFFERING,
        CUED,
        NONE
    }

    /**
     * 自己写一段HTML,并设置好Youtube的视频id,放到WebView中进行显示
     * @param videoId
     * @return
     */
    private String getVideoHTML(String videoId) {
        try {
            InputStream in = getResources().openRawResource(R.raw.players);
            if (in != null) {
                InputStreamReader stream = new InputStreamReader(in, "utf-8");
                BufferedReader buffer = new BufferedReader(stream);
                String read;
                StringBuilder sb = new StringBuilder("");

                while ((read = buffer.readLine()) != null) {
                    sb.append(read + "\n");
                }

                in.close();

                String html = sb.toString().replace("[VIDEO_ID]", videoId).replace("[BG_COLOR]", backgroundColor);
                html = html.replace("[AUTO_PLAY]", String.valueOf(params.getAutoplay())).replace("[AUTO_HIDE]", String.valueOf(params.getAutohide())).replace("[REL]", String.valueOf(params.getRel())).replace("[SHOW_INFO]", String.valueOf(params.getShowinfo())).replace("[ENABLE_JS_API]", String.valueOf(params.getEnablejsapi())).replace("[DISABLE_KB]", String.valueOf(params.getDisablekb())).replace("[CC_LANG_PREF]", String.valueOf(params.getCc_lang_pref())).replace("[CONTROLS]", String.valueOf(params.getControls())).replace("[FS]", String.valueOf(params.getFs()));
                return html;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
}

在Fragment中初始化的代码

View youtubeView = LayoutInflater.from(journalActivity).inflate(R.layout.layout_youtube_player, null);
        YoutubePlayerView youtubePlayerView = (YoutubePlayerView) youtubeView.findViewById(R.id.youtubePlayerView);
        youtubePlayerView.setAutoPlayerHeight(journalActivity);
        youtubePlayerView.initialize(videoID, new YoutubePlayerCallBack(youtubePlayerView), mWebChromeClient);
        ll_journal.addView(youtubeView,ll_journal.getChildCount()-1);

上面提到的布局文件R.layout.layout_youtube_player如下

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:orientation="vertical">

    <com.xxx.YoutubePlayerView
        android:id="@+id/youtubePlayerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_marginTop="10dp"/>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_gravity="top"
        android:clickable="true"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_marginTop="10dp">
    </FrameLayout>

</FrameLayout>
上面提到的WebChromeClient定义如下,用于控制全屏播放的

private WebChromeClient mWebChromeClient = new WebChromeClient(){

        @Override
        public View getVideoLoadingProgressView() {
                LayoutInflater inflater = LayoutInflater.from(activity);
                mVideoProgressView = inflater.inflate(R.layout.video_layout_loading, null);
            
            return mVideoProgressView;
        }

        @Override
        public void onShowCustomView(View view,
                                     WebChromeClient.CustomViewCallback callback) {
            // if a view already exists then immediately terminate the new one
            if(journalActivity==null){
                return;
            }
            if (mCustomView != null) {
                onHideCustomView();
                return;
            }

            // 1. Stash the current state
            mCustomView = view;
            mOriginalSystemUiVisibility = journalActivity.getWindow().getDecorView().getSystemUiVisibility();
            mOriginalOrientation = journalActivity.getRequestedOrientation();

            // 2. Stash the custom view callback
            mCustomViewCallback = callback;

            // 3. Add the custom view to the view hierarchy
            FrameLayout decor = (FrameLayout) journalActivity.getWindow().getDecorView();
            decor.addView(mCustomView, new FrameLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT));
            if(mVideoFullScreenBack!=null){
                mVideoFullScreenBack.setVisibility(View.VISIBLE);
            }

            // 4. Change the state of the window
            activity.getWindow().getDecorView().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);
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        }

        @Override
        public void onHideCustomView() {
            if (journalActivity == null) {
                return;
            }
            // 1. Remove the custom view
            FrameLayout decor = (FrameLayout) journalActivity.getWindow().getDecorView();
            decor.removeView(mCustomView);
            mCustomView = null;
            if(mVideoFullScreenBack!=null){
                mVideoFullScreenBack.setVisibility(View.GONE);
            }

            // 2. Restore the state to it's original form
            journalActivity.getWindow().getDecorView()
                    .setSystemUiVisibility(mOriginalSystemUiVisibility);
            journalActivity.setRequestedOrientation(mOriginalOrientation);

            // 3. Call the custom view callback
            if(mCustomViewCallback!=null){
                mCustomViewCallback.onCustomViewHidden();
                mCustomViewCallback = null;
            }

        }
    };

上面提到的R.layout.view_layout_loading布局文件如下,仅仅是一个progressBar当占位符用的

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:gravity="center"
             android:orientation="vertical">

    <ProgressBar
        android:id="@android:id/progress"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>

</FrameLayout>

从url中抽取VideoId的方法如下

 private String parseIDfromVideoUrl(String videoUrl){
        
        int startIndex = videoUrl.indexOf(VIDEO_ID_START);
        if(startIndex != -1){
            startIndex = startIndex + VIDEO_ID_START.length();
            int endIndex = videoUrl.indexOf("?");
            if(endIndex == -1){
                endIndex = videoUrl.length();
            }
            if(startIndex < endIndex){
                return videoUrl.substring(startIndex,endIndex);
            }
        }
        return "";
    }

因为本项目在同一个fragment中放了好多的这样的视频播放控件,所以为了统一他们暂停,销毁操作,这里使用了一个ArrayList进行维护

当切换到其他fragment或者有新的Activity压到上面的时候暂停WebView的播放,fragment总的onPause方法这么写:

@Override
    public void onPause() {
        if(playerViewList!=null){
            for(YoutubePlayerView v : playerViewList){
                if(v.getPlayerState() == YoutubePlayerView.STATE.PLAYING ){
                    v.pause();
                }else if(v.getPlayerState() == YoutubePlayerView.STATE.BUFFERING){
                    v.stop();
                }
            }
        }
        super.onPause();
    }
还需要让fragment在销毁的时候释放WebView的资源如下:

@Override
    public void onDestroy() {
        super.onDestroy();

        if(playerViewList!=null){
            for(YoutubePlayerView v : playerViewList){
                if(v!=null){
                    v.onDestroy();
                }
            }
        }
}

在按下返回按钮时关闭全屏显示的代码

 @Override
    public void onBackPressed() {
       
                        boolean isClose = currentJournalFragment.closeFullScreen();
                        if(isClose){
                            return;
                        }
         
            }
这个fragment的closeFullScreen方法如下

public boolean closeFullScreen(){
        if(mCustomView!=null && mCustomViewCallback!=null){
            mWebChromeClient.onHideCustomView();
            return true;
        }
        return false;
    }

这里面的mCustomViewCallback 是WebChromeClient的onShowCustomView()方法的第二个参数,使用一个全局变量把它引用起来


版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

Android WebView 视频播放解决

最近项目中遇到WebView 播放视频需求(H5 video 标签),那么问题来了视频无法全屏播放等。 对于这种视频播放问题有多种方案可以解决,例如: 1.实现一个网络视频播放器来播放网络视频(这...

使用HTML5抓取 Audio & Video

原文地址: http://www.html5rocks.com/en/tutorials/getusermedia/intro/  本地化的文章: http://www.html5rocks.com...

精选:深入理解 Docker 内部原理及网络配置

网络绝对是任何系统的核心,对于容器而言也是如此。Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,如 Docker 的镜像管理。然而,Docker的网络一直以来都比较薄弱,所以我们有必要深入了解Docker的网络知识,以满足更高的网络需求。

Documentation目录下binfmt_misc文档的翻译

希望大家多多提提意见

JsBridge最详细的解析,高版本webview的evaluateJavascript

android jsbridge原理详解,并加入API19以上版本的调用方法

google tv上webview页面js实现焦点跳转

webview页面使用javascript实现焦点移动

Ubuntu16.04上安装gradle3.2.1环境详解

Gradle 是以 Groovy 语言为基础,面向Java应用为主。基于DSL(领域特定语言)语法的自动化构建工具。在github上,gradle项目很多,有的是gradel跟maven构建一块儿使用...

Linux下如何指定某一类型程序用特定程序打开(通过binfmt_misc)

在Windows平台上,可以绑定拥有特定扩展名的文件,使用特定的程序打开。比如,PDF文件就使用Acrobat Reader打开。这样做确实极大的方便了用户的使用体验。 其实,在Linux平台上,也提...

安卓webview和js+html交互利用的addJavascriptInterface和webview.loadUrl("javascript:**");

安卓webview和js+html动态添加数据实现交互

安卓TV开发(九) Android模拟事件 遥控器变身成鼠标来操作TV

Android 模拟事件onclick点击 并实现遥控器模模拟鼠标操作,遥控器变身成鼠标。

WebView 细节,onShowCustomView与JS注入来实现视频全屏播放

概述WebView是开发中经常使用的组件,在使用中我们需要考虑 文件下载,自定义scheme,重定向,硬件加速,Cookie,HTTPS/HTTP混合等多种问题。关于 WebView 之前的总结 ...
  • wbwjx
  • wbwjx
  • 2017-01-21 11:50
  • 1622
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)