今天(2016.11.23)再次使用到這個(gè)方法,發(fā)現(xiàn)在js注入時(shí)存在很大可能性的失敗,查閱相關(guān)資料發(fā)現(xiàn),是js注入時(shí)機(jī)問題,最好是在頁面加載到20%-30%開始注入,所以今天增加一些代碼,現(xiàn)在先考慮問題的解決,不考慮性能問題:
webView.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress) {
//循環(huán)注入
if(progress>=20){
webView.loadUrl(BrowserJsInject.fullScreenByJs(luyan_bean.videoPath));
}
}
});
測試結(jié)果發(fā)現(xiàn)該方法對(duì)騰訊視頻注入成功率差不多為100%,反正我測試的沒有發(fā)現(xiàn)不成功的問題。
下面是之前寫的:
公司app(9私聊)里面有一個(gè)頁面需要根據(jù)網(wǎng)址來播放視頻,而且播放的視頻一般都是騰訊視頻,但外包明確表示他們無法實(shí)現(xiàn),只好自己來解決了,最近拿到源碼,花了一天時(shí)間(2016.9.9)解決了這個(gè)問題。
首先我們應(yīng)該了解顯示條件,首先是在WebView加載視頻鏈接,還有就是全屏和退出全屏按鈕必須在網(wǎng)頁中,也就是你必須監(jiān)聽到網(wǎng)頁全屏事件。
此處我將介紹4種方法,我遇到是使用方法4才能真正解決的全屏問題!
收下來看xml文件: activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main"
android:layout_width="match_parent" android:layout_height="match_parent">
<LinearLayout
android:id="@+id/tv3"
android:layout_width="fill_parent"
android:layout_height="200dp"
android:orientation="vertical" >
<WebView
android:id="@+id/my_web"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000" />
</LinearLayout>
</RelativeLayout>
添加訪問網(wǎng)絡(luò)的權(quán)限:
<uses-permission android:name="android.permission.INTERNET"/>
初始化:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
my_web=(WebView)findViewById(R.id.my_web);
initWebView();
my_web.loadUrl("http://v.qq.com/iframe/player.html?vid=o0318tp1ddw&tiny=0&auto=0");
my_web.addJavascriptInterface(new JsObject(MainActivity.this), "console");
//my_web.loadData(s, "text/html; charset=UTF-8", null);
}
private void initWebView() {
WebSettings ws = my_web.getSettings();
/**
* setAllowFileAccess 啟用或禁止WebView訪問文件數(shù)據(jù) setBlockNetworkImage 是否顯示網(wǎng)絡(luò)圖像
* setBuiltInZoomControls 設(shè)置是否支持縮放 setCacheMode 設(shè)置緩沖的模式
* setDefaultFontSize 設(shè)置默認(rèn)的字體大小 setDefaultTextEncodingName 設(shè)置在解碼時(shí)使用的默認(rèn)編碼
* setFixedFontFamily 設(shè)置固定使用的字體 setJavaSciptEnabled 設(shè)置是否支持Javascript
* setLayoutAlgorithm 設(shè)置布局方式 setLightTouchEnabled 設(shè)置用鼠標(biāo)激活被選項(xiàng)
* setSupportZoom 設(shè)置是否支持變焦
* */
ws.setJavaScriptCanOpenWindowsAutomatically(true);
ws.setPluginState(WebSettings.PluginState.ON);
// settings.setPluginsEnabled(true);
ws.setAllowFileAccess(true);
ws.setLoadWithOverviewMode(true);
ws.setBuiltInZoomControls(true);// 隱藏縮放按鈕
ws.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);// 排版適應(yīng)屏幕
ws.setUseWideViewPort(true);// 可任意比例縮放
ws.setLoadWithOverviewMode(true);// setUseWideViewPort方法設(shè)置webview推薦使用的窗口。setLoadWithOverviewMode方法是設(shè)置webview加載的頁面的模式。
ws.setSavePassword(true);
ws.setSaveFormData(true);// 保存表單數(shù)據(jù)
ws.setJavaScriptEnabled(true);
ws.setDomStorageEnabled(true);
my_web.setSaveEnabled(false);
ws.setSaveFormData(false);
// 下面的一句話是必須的,必須要打開javaScript不然所做一切都是徒勞的
ws.setJavaScriptEnabled(true);
ws.setSupportZoom(false);
xwebchromeclient = new xWebChromeClient();
//setWebChromeClient主要處理解析,渲染網(wǎng)頁等瀏覽器做的事情
//這個(gè)方法必須有,就算類中沒有函數(shù)也可以,不然視頻播放不了
my_web.setWebChromeClient(xwebchromeclient);
//WebChromeClient是輔助WebView處理Javascript的對(duì)話框,網(wǎng)站圖標(biāo),網(wǎng)站title,加載進(jìn)度等
my_web.setWebViewClient(new xWebViewClientent());
}
1.my_web = (FastWebView) findViewById(R.id.my_web);
my_web.addJavascriptInterface(new JsObject(LuyanDetailActivity.this), "console");
- android:configChanges="orientation|keyboardHidden|screenSize"
方法1:
在綁定的WebChromeClient子類中調(diào)用onShowCustomView方法,在該方法中進(jìn)行視頻的全屏操作,例如代碼:
// 進(jìn)入全屏的時(shí)候
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
// 賦值給callback
customViewCallback = callback;
// 設(shè)置webView隱藏
webview.setVisibility(View.GONE);
// 聲明video,把之后的視頻放到這里面去
FrameLayout video = (FrameLayout) findViewById(R.id.video);
// 將video放到當(dāng)前視圖中
video.addView(view);
// 橫屏顯示
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// 設(shè)置全屏
setFullScreen();
}
// 退出全屏的時(shí)候
@Override
public void onHideCustomView() {
if (customViewCallback != null) {
// 隱藏掉
customViewCallback.onCustomViewHidden();
}
// 用戶當(dāng)前的首選方向
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
// 退出全屏
quitFullScreen();
// 設(shè)置WebView可見
webview.setVisibility(View.VISIBLE);
}
但我在使用該方法播放騰訊視頻并全屏?xí)r,onShowCustomView方法和onHideCustomView方法都不會(huì)被調(diào)用。
方法2:
向頁面注入js事件(例如alert),注入方法參照下面方法3中事件的注入,捕獲頁面alert,例如代碼:
class MyWebChromeClient extends WebChromeClient{
@Override
public boolean onJsAlert(WebView view, String url, String message,
JsResult result) {
Builder builder=new AlertDialog.Builder(JqueryMobile01Activity.this);
builder.setTitle("自定義alert事件");
builder.show();
return super.onJsAlert(view, url, message, result);
}
同方法1一樣,我實(shí)際操作時(shí),也沒有調(diào)用onJsAlert事件,
方法3:
參照網(wǎng)址:http://blog.csdn.net/lx331675996/article/details/50634670
JS注入部分:
在網(wǎng)頁完成加載時(shí),會(huì)回調(diào)onPageFinish()方法,在這里可以注入自己需要的js方法。
@Override
public void onPageFinished(WebView view, String url) {
view.loadUrl(BrowserJsInject.fullScreenByJs(url));
}
這里的BrowserJsInject是我自己寫的一個(gè)對(duì)于JS注入的公共類,里面內(nèi)容如下:
public class BrowserJsInject {
/**
* Js注入
* @param url 加載的網(wǎng)頁地址
* @return 注入的js內(nèi)容,若不是需要適配的網(wǎng)址則返回空javascript
*/
public static String fullScreenByJs(String url){
String refer = referParser(url);
if (null != refer) {
return "javascript:document.getElementsByClassName('" + referParser(url) + "')[0].addEventListener('click',function(){local_obj.playing();return false;});";
}else {
return "javascript:";
}
}
/**
* 對(duì)不同的視頻網(wǎng)站分析相應(yīng)的全屏控件
* @param url 加載的網(wǎng)頁地址
* @return 相應(yīng)網(wǎng)站全屏按鈕的class標(biāo)識(shí)
*/
public static String referParser(String url){
if (url.contains("letv")) {
return "hv_ico_screen"; //樂視Tv
}else if (url.contains("youku")) {
return "x-zoomin";//優(yōu)酷
}else if (url.contains("bilibili")) {
return "icon-widescreen"; //bilibili
}else if (url.contains("qq")) {
return "tvp_fullscreen_button"; //騰訊視頻
}
return null;
}
}
fullScreen(String url)方法將傳入的加載的url,通過referParser(String url)方法分析網(wǎng)站類型。referParser中的返回值都是我個(gè)人對(duì)于不同網(wǎng)站JS調(diào)試后找到的,有問題歡迎指出。然后根據(jù)不同的網(wǎng)站注入不同的js,實(shí)現(xiàn)對(duì)全屏按鈕的監(jiān)聽操作。其中js字符串中l(wèi)ocal_obj為客戶端綁定的相關(guān)類,用于處理js操作。 至此,JS注入部分結(jié)束。
Android客戶端處理JS綁定:
當(dāng)JS注入完成后,網(wǎng)頁就已經(jīng)運(yùn)行了這個(gè)腳本,對(duì)相應(yīng)的全屏按鈕實(shí)現(xiàn)了監(jiān)聽。 在設(shè)置webview的相關(guān)屬性操作時(shí),可以對(duì)設(shè)置相應(yīng)的JS操作:
mMainView.addJavascriptInterface(new Object(){
@JavascriptInterface
public void playing(){
Log.i("video", "=======================");
fullScreen(true);
}
}, "local_obj");
然后,在點(diǎn)擊全屏按鈕時(shí)就會(huì)觸發(fā)fullScreen()方法,這個(gè)方法中處理你所需要實(shí)現(xiàn)的相關(guān)全屏操作。
比如,全屏并手機(jī)橫屏:
public void fullScreen(){
if (mActivity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
mCustomScreenLinearLayout.scrollTo(0, 0);
mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
mUrlBarAutoShowManager.flag = false;
mFixedTitlebarContainer.setVisibility(View.GONE);
showFixTitle(false);
mBottomNavigation.hideBottomNav();
mBottomFrameLayout.setVisibility(View.GONE);
}else {
mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mUrlBarAutoShowManager.flag = true;
mFixedTitlebarContainer.setVisibility(View.VISIBLE);
hideFixTitle(false);
mBottomNavigation.showBottomNav();
mBottomFrameLayout.setVisibility(View.VISIBLE);
}
}
試了一下,還需行不通,因?yàn)閘ocal_obj.playing();語句在js中會(huì)報(bào)錯(cuò),就算把它放到一個(gè)html頁面中,也是會(huì)報(bào)錯(cuò)。
方法4:重點(diǎn);來了
我們?cè)诜椒?的基礎(chǔ)上進(jìn)行修改,既然local_obj.playing();事件會(huì)報(bào)錯(cuò),那我執(zhí)行一個(gè)js自帶的事件不就行了,例如:console.log();
下面貼出我自己源代碼:
MainActivity.class
package a0.android5.wevview2;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.WindowManager;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class MainActivity extends AppCompatActivity {
private WebView my_web;
xWebChromeClient xwebchromeclient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
my_web=(WebView)findViewById(R.id.my_web);
initWebView();
my_web.loadUrl("http://v.qq.com/iframe/player.html?vid=o0318tp1ddw&tiny=0&auto=0");
my_web.addJavascriptInterface(new JsObject(MainActivity.this), "console");
//my_web.loadData(s, "text/html; charset=UTF-8", null);
}
private void initWebView() {
WebSettings ws = my_web.getSettings();
/**
* setAllowFileAccess 啟用或禁止WebView訪問文件數(shù)據(jù) setBlockNetworkImage 是否顯示網(wǎng)絡(luò)圖像
* setBuiltInZoomControls 設(shè)置是否支持縮放 setCacheMode 設(shè)置緩沖的模式
* setDefaultFontSize 設(shè)置默認(rèn)的字體大小 setDefaultTextEncodingName 設(shè)置在解碼時(shí)使用的默認(rèn)編碼
* setFixedFontFamily 設(shè)置固定使用的字體 setJavaSciptEnabled 設(shè)置是否支持Javascript
* setLayoutAlgorithm 設(shè)置布局方式 setLightTouchEnabled 設(shè)置用鼠標(biāo)激活被選項(xiàng)
* setSupportZoom 設(shè)置是否支持變焦
* */
ws.setJavaScriptCanOpenWindowsAutomatically(true);
ws.setPluginState(WebSettings.PluginState.ON);
// settings.setPluginsEnabled(true);
ws.setAllowFileAccess(true);
ws.setLoadWithOverviewMode(true);
ws.setBuiltInZoomControls(true);// 隱藏縮放按鈕
ws.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);// 排版適應(yīng)屏幕
ws.setUseWideViewPort(true);// 可任意比例縮放
ws.setLoadWithOverviewMode(true);// setUseWideViewPort方法設(shè)置webview推薦使用的窗口。setLoadWithOverviewMode方法是設(shè)置webview加載的頁面的模式。
ws.setSavePassword(true);
ws.setSaveFormData(true);// 保存表單數(shù)據(jù)
ws.setJavaScriptEnabled(true);
ws.setDomStorageEnabled(true);
my_web.setSaveEnabled(false);
ws.setSaveFormData(false);
// 下面的一句話是必須的,必須要打開javaScript不然所做一切都是徒勞的
ws.setJavaScriptEnabled(true);
ws.setSupportZoom(false);
xwebchromeclient = new xWebChromeClient();
//setWebChromeClient主要處理解析,渲染網(wǎng)頁等瀏覽器做的事情
//這個(gè)方法必須有,就算類中沒有函數(shù)也可以,不然視頻播放不了
my_web.setWebChromeClient(xwebchromeclient);
//WebChromeClient是輔助WebView處理Javascript的對(duì)話框,網(wǎng)站圖標(biāo),網(wǎng)站title,加載進(jìn)度等
my_web.setWebViewClient(new xWebViewClientent());
}
/**
* 處理Javascript的對(duì)話框、網(wǎng)站圖標(biāo)、網(wǎng)站標(biāo)題以及網(wǎng)頁加載進(jìn)度等
* @author
*/
public class xWebChromeClient extends WebChromeClient {
}
/**
* 設(shè)置監(jiān)聽事件
* 處理各種通知、請(qǐng)求等事件
* @author
*/
public class JsObject {
Context mContext;
JsObject(Context c) {
mContext = c;
}
@JavascriptInterface
public void log(){
System.out.println("返回結(jié)果");
setFullScreen();
}
}
/**
* 設(shè)置全屏
*/
private void setFullScreen() {
Log.i("視頻全屏-->", "豎屏切換到橫屏");
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// 設(shè)置全屏的相關(guān)屬性,獲取當(dāng)前的屏幕狀態(tài),然后設(shè)置全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 全屏下的狀態(tài)碼:1098974464
// 窗口下的狀態(tài)嗎:1098973440
}
public class xWebViewClientent extends WebViewClient {
@Override
public void onPageFinished(WebView view, String url) {
view.loadUrl(BrowserJsInject.fullScreenByJs(url));
System.out.println("url1:"+BrowserJsInject.fullScreenByJs(url));
System.out.println("url2"+url);
//view.loadData("", "text/html", "UTF-8");
// my_web.loadUrl("http://v.qq.com/iframe/player.html?vid=o0318tp1ddw&tiny=0&auto=0");
//view.loadUrl("javascript:alert('123')");
//view.loadUrl(BrowserJsInject.fullScreenByJs(url));
/*//點(diǎn)擊鏈接時(shí):
view.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url); //在當(dāng)前的webview中跳轉(zhuǎn)到新的url
System.out.println("鏈接--》"+url);
return true;
}
});
啟動(dòng)手機(jī)瀏覽器來打開新的url
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
return true;
}
});
*/
/*
view.addJavascriptInterface(new Object(){
@JavascriptInterface
public void playing(){
System.out.println("返回結(jié)果");
setFullScreen();
}
}, "local_obj");
view.loadUrl(BrowserJsInject.fullScreenByJs(url));
System.out.println("url1:"+BrowserJsInject.fullScreenByJs(url));
System.out.println("url2"+url);*/
//myJavaScriptInterface = new JavaScriptInterface(this);
}
/**
* 設(shè)置全屏
*/
private void setFullScreen() {
Log.i("視頻全屏-->", "豎屏切換到橫屏");
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// 設(shè)置全屏的相關(guān)屬性,獲取當(dāng)前的屏幕狀態(tài),然后設(shè)置全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
}
BrowserJsInject.class
package a0.android5.wevview2;
/**
* Created by 趙理想on 2016/9/8.
* 個(gè)人郵箱:1782980833@qq.com
* *所屬公司:長江期貨
* 該類用途:
*/
public class BrowserJsInject {
/**
* Js注入
* @param url 加載的網(wǎng)頁地址
* @return 注入的js內(nèi)容,若不是需要適配的網(wǎng)址則返回空javascript
*/
public static String fullScreenByJs(String url){
String refer = referParser(url);
if (null != refer) {
String js3="window.onload=function(){document.getElementsByClassName('"
+ referParser(url) + "')[0].addEventListener('click',function(){alert('120');" +
"console.log();" +
"alert('110');})}"
+ ";";
return "javascript:"+js3;
}else {
return "javascript:";
}
}
/**
* 對(duì)不同的視頻網(wǎng)站分析相應(yīng)的全屏控件
* @param url 加載的網(wǎng)頁地址
* @return 相應(yīng)網(wǎng)站全屏按鈕的class標(biāo)識(shí)
*/
public static String referParser(String url){
if (url.contains("letv")) {
return "hv_ico_screen"; //樂視Tv
}else if (url.contains("youku")) {
return "x-zoomin";//優(yōu)酷
}else if (url.contains("bilibili")) {
return "icon-widescreen"; //bilibili
}else if (url.contains("qq")) {
return "tvp_fullscreen_button"; //騰訊視頻
}
return null;
}
}
特別提醒:
1.別忘記屏幕旋轉(zhuǎn)會(huì)影響聲明周期,可以參考我的另一篇文章http://www.itdecent.cn/p/2370426eac88
2.my_web.addJavascriptInterface(new JsObject(MainActivity.this), "console");
這句必須放在my_web.loadUrl("http://v.qq.com/iframe/player.html?vid=o0318tp1ddw&tiny=0&auto=0");下面。
哈哈,終于成功了??!