实现效果

项目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 {@JavascriptInterfacepublic void onReady(String arg) {JLogUtils.d(TAG, "onReady(" + arg + ")");if (youTubeListener != null) {youTubeListener.onReady();}}@JavascriptInterfacepublic 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);}}@JavascriptInterfacepublic void onPlaybackQualityChange(String arg) {JLogUtils.d(TAG, "onPlaybackQualityChange(" + arg + ")");if (youTubeListener != null) {youTubeListener.onPlaybackQualityChange(arg);}}@JavascriptInterfacepublic void onPlaybackRateChange(String arg) {JLogUtils.d(TAG, "onPlaybackRateChange(" + arg + ")");if (youTubeListener != null) {youTubeListener.onPlaybackRateChange(arg);}}@JavascriptInterfacepublic void onError(String arg) {JLogUtils.e(TAG, "onError(" + arg + ")");if (youTubeListener != null) {youTubeListener.onError(arg);}}@JavascriptInterfacepublic void onApiChange(String arg) {JLogUtils.d(TAG, "onApiChange(" + arg + ")");if (youTubeListener != null) {youTubeListener.onApiChange(arg);}}@JavascriptInterfacepublic void currentSeconds(String seconds) {if (youTubeListener != null) {youTubeListener.onCurrentSecond(Double.parseDouble(seconds));}}@JavascriptInterfacepublic void duration(String seconds) {if (youTubeListener != null) {youTubeListener.onDuration(Double.parseDouble(seconds));}}@JavascriptInterfacepublic 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() {@Overridepublic 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 {@JavascriptInterfacepublic void onReady(String arg) {Log.d(TAG, "onReady(" + arg + ")");if (youTubeListener != null) {youTubeListener.onReady();}}@JavascriptInterfacepublic 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);}}@JavascriptInterfacepublic void onPlaybackQualityChange(String arg) {Log.d(TAG, "onPlaybackQualityChange(" + arg + ")");if (youTubeListener != null) {youTubeListener.onPlaybackQualityChange(arg);}}@JavascriptInterfacepublic void onPlaybackRateChange(String arg) {Log.d(TAG, "onPlaybackRateChange(" + arg + ")");if (youTubeListener != null) {youTubeListener.onPlaybackRateChange(arg);}}@JavascriptInterfacepublic void onError(String arg) {Log.e(TAG, "onError(" + arg + ")");if (youTubeListener != null) {youTubeListener.onError(arg);}}@JavascriptInterfacepublic void onApiChange(String arg) {Log.d(TAG, "onApiChange(" + arg + ")");if (youTubeListener != null) {youTubeListener.onApiChange(arg);}}@JavascriptInterfacepublic void currentSeconds(String seconds) {if (youTubeListener != null) {youTubeListener.onCurrentSecond(Double.parseDouble(seconds));}}@JavascriptInterfacepublic void duration(String seconds) {if (youTubeListener != null) {youTubeListener.onDuration(Double.parseDouble(seconds));}}@JavascriptInterfacepublic 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 destroyedyouTubeListener = 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);}@Overridepublic 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;}@Overridepublic 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.YoutubePlayerViewandroid: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"/><FrameLayoutandroid: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(){@Overridepublic View getVideoLoadingProgressView() {LayoutInflater inflater = LayoutInflater.from(activity);mVideoProgressView = inflater.inflate(R.layout.video_layout_loading, null);return mVideoProgressView;}@Overridepublic void onShowCustomView(View view,WebChromeClient.CustomViewCallback callback) {// if a view already exists then immediately terminate the new oneif(journalActivity==null){return;}if (mCustomView != null) {onHideCustomView();return;}// 1. Stash the current statemCustomView = view;mOriginalSystemUiVisibility = journalActivity.getWindow().getDecorView().getSystemUiVisibility();mOriginalOrientation = journalActivity.getRequestedOrientation();// 2. Stash the custom view callbackmCustomViewCallback = callback;// 3. Add the custom view to the view hierarchyFrameLayout 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 windowactivity.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);}@Overridepublic void onHideCustomView() {if (journalActivity == null) {return;}// 1. Remove the custom viewFrameLayout 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 formjournalActivity.getWindow().getDecorView().setSystemUiVisibility(mOriginalSystemUiVisibility);journalActivity.setRequestedOrientation(mOriginalOrientation);// 3. Call the custom view callbackif(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"><ProgressBarandroid: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方法这么写:

@Overridepublic 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的资源如下:

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

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

 @Overridepublic 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实现Youtube网络视频播放控件相关推荐

  1. Android技术分享| 【Android 自定义View】多人视频通话控件

    [Android 自定义View]多人视频通话控件 *以上图片截自微信等待中界面 等待中界面 上图是微信多人视频通话时未接通的界面状态,可见每个人的 View 中大致需包含了以下元素. 头像 昵称 L ...

  2. Android自定义ViewGroup实现朋友圈九宫格控件

    在我们的实际应用中,经常需要用到自定义控件,比如自定义圆形头像,自定义计步器等等,这篇文章主要给大家介绍了关于Android自定义ViewGroup实现朋友圈九宫格控件的相关资料,需要的朋友可以参考下 ...

  3. 《android多媒体api》之VideoView 视频播放控件

    <android多媒体api>系列是整合梳理android开发中经常用到的媒体相关api:多媒体开发主要内容有音频.视频录制播放.摄像头操作.录制操作.流媒体.直播.推流.拉流等方面:最近 ...

  4. Android自定义类似支付宝密码输入的控件

    前言:感觉自己记忆力不是很好,很多写过的东西容易忘记,所以还是写个博客记录一下,菜鸟一枚,望各位师傅指点. 嘿喂狗,来看我们要实现的效果(如果你说:"开发之前当然看不到效果,看个diao啊& ...

  5. Android自定义View系列之进度指示控件

    我开通微信公众号啦,如果大家喜欢我的文章,欢迎大家关注我的微信号,我会定期为大家推送Android中的热门知识. 今天为大家介绍另一个自定义View--进度指示器,这个在电商App和支付宝等中经常遇到 ...

  6. Android自定义View实现下拉刷新控件

    路过的老铁同志可以微信搜索"Android小菜",不定期更新Android技术文章.比CSDN更快一步阅读. 本文实现的功能如下: 1.支持下拉刷新: 2.支持上拉加载更多 3.刷 ...

  7. 玩转Android之在线视频播放控件Vitamio的使用

    其实Android中自带的MediaPlayer本身就能播放在线视频,MediaPlayer结合SurfaceView播放在线视频也是不错的选择(如果你没有性能或者用户体验上的要求),关于MediaP ...

  8. android播放视频控件,视频播放控件VideoView的基本使用

    在Android的界面控件中有一个视频播放控件,可以直接在手机上面开辟一个视频播放的UI,播放视频,下面ATAAW.COM大概介绍下视频控件VideoView的使用,由于视频播放是属于Android多 ...

  9. 15个iOS的视频播放控件

    2019独角兽企业重金招聘Python工程师标准>>> 精心挑选了15个人气最高的iOS视频播放控件.让你在开发视频播放功能的时候自由翱翔. ijkplayer - Bilibili ...

  10. Android基础学习(二)—— 常用控件

    TextView 显示文本内容 Button 按钮 EditText 输入框 ImageView 显示图片 ProgressBar 进度条 ToolBar 自定义标题栏 AlertDialog 对话框 ...

最新文章

  1. R语言使用ggplot2包geom_jitter()函数绘制分组(strip plot,一维散点图)带状图(自定义色彩、形状)实战
  2. Smali源代码分析教程(转)
  3. plt title设置在下方_Python数据分析:可视化图表注释设置
  4. VMprotect静态跟踪 字节码反编译
  5. 夜深了,发点无聊的东西
  6. linux中端口的欺骗,Linux中的端口占用问题
  7. 设计类超实用的导航网站,一网包含1000+个行业内热门资讯灵感源!
  8. linux php7 yum 卸载,Linux_在Centos中yum安装和卸载软件的使用方法,安装一个软件时 yum -y install h - phpStudy...
  9. win2003 sp2 设置NLB
  10. 复制出来的虚拟机加入域提示试图加入域的SID与本计算机的SID相同解决方法
  11. poi生成excel并让excel的列有筛选功能
  12. JS 今天/明天的日期
  13. Python 判断无向图是否存在环
  14. references column 'xxx' which is not in SELECT list
  15. windows快速切换jdk版本号
  16. 培训直播平台这些功能是必不可少的
  17. 什么是MapReduce,MapReduce的工作流程和原理是什么
  18. 当敦煌月牙泉遍布Wi-Fi:一个“新型智慧城市”打开方式是这样的
  19. 个人向前端知识“复健”
  20. 简单练习---学生课程信息

热门文章

  1. 改善网页性能的5种方法
  2. 【vue网站优化】秒开网页
  3. 常见的java毕业设计_php毕业设计题目汇总
  4. 八爪鱼采集ajax表格,怎么采集八爪鱼采集器单网页表格信息
  5. linpack性能测试记录
  6. winrar命令行加压解密
  7. 测试投入度量元的选择
  8. 成为软件架构师需要什么?
  9. java 下载暂停实现_在文件下载中实现暂停/恢复
  10. easyui textbox onblur失效