Android webView 播放 YouTube 视频
Posted
技术标签:
【中文标题】Android webView 播放 YouTube 视频【英文标题】:Android webView to play YouTube videos 【发布时间】:2012-12-14 20:19:45 【问题描述】:谁能帮助使用此代码来播放嵌入在网站中的 youtube 视频? 已经尝试了各种东西,包括清单中的 hardwareAccelerated=true。
webview 显示 youtube 页面和缩略图,但点击它们时没有任何反应。
在某些硬件加速的设备上,它播放声音但不播放视频。 谢谢大家!
package com.inglesupdated;
import java.util.ArrayList;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Picture;
import android.net.Uri;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.WebView.PictureListener;
import android.widget.Button;
import android.widget.LinearLayout;
public class BT_screen_customURL extends BT_activity_base
private boolean didCreate = false;
private WebView webView = null;
private String dataURL = "";
private String currentURL = "";
private String originalURL = "";
private AlertDialog confirmLaunchInNativeAppDialogue = null;
private AlertDialog confirmEmailDocumentDialogue = null;
private String showBrowserBarBack = "";
private String showBrowserBarLaunchInNativeApp = "";
private String showBrowserBarEmailDocument = "";
private String showBrowserBarRefresh = "";
//////////////////////////////////////////////////////////////////////////
//activity life-cycle events.
//onCreate
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
this.activityName = "BT_screen_customURL";
BT_debugger.showIt(activityName + ":onCreate");
//reference to base layout..
LinearLayout baseView = (LinearLayout)findViewById(R.id.baseView);
//setup background colors...
BT_viewUtilities.updateBackgroundColorsForScreen(this, this.screenData);
//setup background images..
if(backgroundImageWorkerThread == null)
backgroundImageWorkerThread = new BackgroundImageWorkerThread();
backgroundImageWorkerThread.start();
//setup navigation bar...
LinearLayout navBar = BT_viewUtilities.getNavBarForScreen(this, this.screenData);
if(navBar != null)
baseView.addView(navBar);
//inflate this screens layout file...
LayoutInflater vi = (LayoutInflater)thisActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View thisScreensView = vi.inflate(R.layout.screen_customurl, null);
//add the view to the base view...
baseView.addView(thisScreensView);
//reference to the webview in the layout file.
webView = (WebView) thisScreensView.findViewById(R.id.webView);
webView.setBackgroundColor(0);
webView.setInitialScale(0);
webView.getSettings().setjavascriptEnabled(true);
webView.getSettings().setSupportZoom(true);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setPluginsEnabled(true);
webView.setPictureListener(new MyPictureListener());
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient()
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
//remember the URL...
currentURL = url;
//load the URL in the app's built-in browser if it's in our list of types to load...
if(BT_act_controller.canLoadDocumentInWebView(url))
//load url in built-in browser...
// showProgress(null, null);
return false;
else
//ask user what app to open this in if the method returned NO...
try
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(Intent.createChooser(i, getString(R.string.openWithWhatApp)));
catch(Exception e)
BT_debugger.showIt(activityName + ": Error launching native app for url: " + url);
showAlert(getString(R.string.noNativeAppTitle), getString(R.string.noNativeAppDescription));
//do not try to load the URL..
return true;
@Override
public void onPageFinished(WebView view, String url)
hideProgress();
BT_debugger.showIt(activityName + ":onPageFinished finished Loading: " + url);
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
hideProgress();
showAlert(getString(R.string.errorTitle), getString(R.string.errorLoadingScreen));
BT_debugger.showIt(activityName = ":onReceivedError ERROR loading url: " + failingUrl + " Description: " + description);
);
//fill JSON properties...
dataURL = BT_strings.getJsonPropertyValue(this.screenData.getJsonObject(), "dataURL", "");
currentURL = dataURL;
originalURL = dataURL;
//button options for hardware menu key...
showBrowserBarBack = BT_strings.getJsonPropertyValue(this.screenData.getJsonObject(), "showBrowserBarBack", "0");
showBrowserBarLaunchInNativeApp = BT_strings.getJsonPropertyValue(this.screenData.getJsonObject(), "showBrowserBarLaunchInNativeApp", "0");
showBrowserBarEmailDocument = BT_strings.getJsonPropertyValue(this.screenData.getJsonObject(), "showBrowserBarEmailDocument", "0");
showBrowserBarRefresh = BT_strings.getJsonPropertyValue(this.screenData.getJsonObject(), "showBrowserBarRefresh", "0");
//prevent user interaction?
String preventUserInteraction = BT_strings.getJsonPropertyValue(this.screenData.getJsonObject(), "preventUserInteraction", "0");
if(preventUserInteraction.equalsIgnoreCase("1"))
//can't seem to get Android to "prevent user interaction"...???
//hide scroll bars..
String hideVerticalScrollBar = BT_strings.getJsonPropertyValue(this.screenData.getJsonObject(), "hideVerticalScrollBar", "0");
if(hideVerticalScrollBar.equalsIgnoreCase("1"))
webView.setVerticalScrollBarEnabled(false);
String hideHorizontalScrollBar = BT_strings.getJsonPropertyValue(this.screenData.getJsonObject(), "hideHorizontalScrollBar", "0");
if(hideHorizontalScrollBar.equalsIgnoreCase("1"))
webView.setHorizontalScrollBarEnabled(false);
String preventAllScrolling = BT_strings.getJsonPropertyValue(this.screenData.getJsonObject(), "preventAllScrolling", "0");
if(preventAllScrolling.equalsIgnoreCase("1"))
webView.setVerticalScrollBarEnabled(false);
webView.setHorizontalScrollBarEnabled(false);
//figure out what to load...
if(dataURL.length() > 1)
String useUrl = BT_strings.mergeBTVariablesInString(dataURL);
BT_debugger.showIt(activityName + ": loading URL from: " + useUrl);
this.loadUrl(useUrl);
else
BT_debugger.showIt(activityName + ": No URL found? Not loading web-view!");
showAlert(getString(R.string.errorTitle), getString(R.string.errorLoadingScreen));
//flag as created..
didCreate = true;
/*
* *******************************************************************
Notes: This screen has fully loaded it's layout file at this point.
BT_activity_base loaded act_base.xml
--THEN--
this screen loaded it's own layout file.
JSON properties for this screen from the app's configuration data are
available for use. You can read these properties easily using the BT_strings class.
Example:
String thisScreensNavBarTitleText = BT_strings.getJsonPropertyValue(this.screenData.getJsonObject(), "thisScreensNavBarTitleText", "default value here");
The screens background and title bar were setup using the BT_viewUtilities class:
BT_viewUtilities.updateBackgroundColorsForScreen()
You are free to modify the layout file and extend this screen however you want.
********************************************************************
*/
//onCreate
//onStart
@Override
protected void onStart()
//BT_debugger.showIt(activityName + ":onStart");
super.onStart();
//onResume
@Override
public void onResume()
super.onResume();
//BT_debugger.showIt(activityName + ":onResume");
//verify onCreate already ran...
if(didCreate)
//onPause
@Override
public void onPause()
super.onPause();
//BT_debugger.showIt(activityName + ":onPause");
//onStop
@Override
protected void onStop()
super.onStop();
//BT_debugger.showIt(activityName + ":onStop");
//onDestroy
@Override
public void onDestroy()
super.onDestroy();
//BT_debugger.showIt(activityName + ":onDestroy");
//end activity life-cycle events
//////////////////////////////////////////////////////////////////////////
//load URL in webView
public void loadUrl(String theUrl)
BT_debugger.showIt(activityName + ": loadUrl");
try
webView.loadUrl(theUrl);
catch(Exception e)
BT_debugger.showIt(activityName + ":loadUrl Exception: " + e.toString());
/* Hardware Back-Key, uncomment as needed.
Uncomment this to make the hardware back-key act like a browser back button
Warning: If you do this the user will need to go "back" the number of times they
when "forward" to return to the previous screen.
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack())
webView.goBack();
return true;
//not the back-key..
return super.onKeyDown(keyCode, event);
*/
//back button...
public void handleBackButton()
BT_debugger.showIt(activityName + ":handleBackButton");
if(webView.canGoBack())
webView.goBack();
else
BT_debugger.showIt(activityName + ":handleBackButton cannot go back?");
//refresh button...
public void handleRefreshButton()
BT_debugger.showIt(activityName + ":handleRefreshButton");
if(currentURL.length() > 1)
showProgress(null, null);
webView.loadUrl(currentURL);
else
BT_debugger.showIt(activityName + ":handleRefreshButton cannot refresh?");
//launch in native app button...
public void handleLaunchInNativeAppButton()
BT_debugger.showIt(activityName + ":handleLaunchInNativeAppButton");
if(currentURL.length() > 1 && originalURL.length() > 1)
confirmLaunchInNativeApp();
else
showAlert(getString(R.string.errorTitle), getString(R.string.cannotOpenDocumentInNativeApp));
BT_debugger.showIt(activityName + ":handleLaunchInNativeAppButton NO url?");
//handle email button
public void handleEmailDocumentButton()
BT_debugger.showIt(activityName + ":handleLaunchInNativeAppButton");
showAlert(getString(R.string.errorTitle), getString(R.string.cannotEmailDocument));
BT_debugger.showIt(activityName + ":handleEmailDocumentButton NO document to email on URL screens");
//confirm launch in native app
public void confirmLaunchInNativeApp()
confirmLaunchInNativeAppDialogue = new AlertDialog.Builder(this).create();
confirmLaunchInNativeAppDialogue.setTitle(getString(R.string.confirm));
confirmLaunchInNativeAppDialogue.setMessage(getString(R.string.confirmLaunchInNativeBrowser));
confirmLaunchInNativeAppDialogue.setIcon(R.drawable.icon);
//YES
confirmLaunchInNativeAppDialogue.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.yes), new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog, int which)
confirmLaunchInNativeAppDialogue.dismiss();
//tell Android to load the URL in the best available Native App...
try
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(currentURL));
startActivity(i);
catch(Exception e)
BT_debugger.showIt(activityName + ": Error launching native app for url: " + currentURL);
showAlert(getString(R.string.noNativeAppTitle), getString(R.string.noNativeAppDescription));
);
//NO
confirmLaunchInNativeAppDialogue.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.no), new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog, int which)
confirmLaunchInNativeAppDialogue.dismiss();
);
//show the confirmation box...
confirmLaunchInNativeAppDialogue.show();
//confirm email document
public void confirmEmailDocumentDialogue()
confirmEmailDocumentDialogue = new AlertDialog.Builder(this).create();
confirmEmailDocumentDialogue.setTitle(getString(R.string.confirm));
confirmEmailDocumentDialogue.setMessage(getString(R.string.confirmEmailDocument));
confirmEmailDocumentDialogue.setIcon(R.drawable.icon);
//YES
confirmEmailDocumentDialogue.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.yes), new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog, int which)
confirmEmailDocumentDialogue.dismiss();
//tell Android launch the native email application...
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("plain/text");
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getString(R.string.sharingWithYou));
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, currentURL);
//chooser will propmpt user if they have more than one email client..
startActivity(Intent.createChooser(emailIntent, getString(R.string.openWithWhatApp)));
);
//NO
confirmEmailDocumentDialogue.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.no), new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog, int which)
confirmEmailDocumentDialogue.dismiss();
);
//show the confirmation box...
confirmEmailDocumentDialogue.show();
//see webView (above) where onPageFinished is loading too soon...
class MyPictureListener implements PictureListener
public void onNewPicture(WebView view, Picture arg1)
//BT_debugger.showIt("here" + ":onNewPicture done");
hideProgress();
/////////////////////////////////////////////////////
//options menu (hardware menu-button)
@Override
public boolean onPrepareOptionsMenu(Menu menu)
super.onPrepareOptionsMenu(menu);
//set up dialog
final Dialog dialog = new Dialog(this);
//linear layout holds all the options...
LinearLayout optionsView = new LinearLayout(this);
optionsView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
optionsView.setOrientation(LinearLayout.VERTICAL);
optionsView.setGravity(Gravity.CENTER_VERTICAL);
optionsView.setPadding(0, 0, 0, 20);
//options have individual layout params
LinearLayout.LayoutParams btnLayoutParams = new LinearLayout.LayoutParams(400, LayoutParams.WRAP_CONTENT);
btnLayoutParams.setMargins(10, 10, 10, 10);
btnLayoutParams.leftMargin = 10;
btnLayoutParams.rightMargin = 10;
btnLayoutParams.topMargin = 0;
btnLayoutParams.bottomMargin = 10;
//holds all the options
ArrayList<Button> options = new ArrayList<Button>();
//cancel...
final Button btnCancel = new Button(this);
btnCancel.setText(getString(R.string.okClose));
btnCancel.setOnClickListener(new OnClickListener()
public void onClick(View v)
dialog.cancel();
);
options.add(btnCancel);
//back...
if(showBrowserBarBack.equalsIgnoreCase("1"))
final Button btn = new Button(this);
btn.setText(getString(R.string.browserBack));
btn.setOnClickListener(new OnClickListener()
public void onClick(View v)
dialog.cancel();
handleBackButton();
);
options.add(btn);
//launch in native app...
if(showBrowserBarLaunchInNativeApp.equalsIgnoreCase("1"))
final Button btn = new Button(this);
btn.setText(getString(R.string.browserOpenInNativeApp));
btn.setOnClickListener(new OnClickListener()
public void onClick(View v)
dialog.cancel();
handleLaunchInNativeAppButton();
);
options.add(btn);
//email document...
if(showBrowserBarEmailDocument.equalsIgnoreCase("1"))
final Button btn = new Button(this);
btn.setText(getString(R.string.browserEmailDocument));
btn.setOnClickListener(new OnClickListener()
public void onClick(View v)
dialog.cancel();
handleEmailDocumentButton();
);
options.add(btn);
//refresh page...
if(showBrowserBarRefresh.equalsIgnoreCase("1"))
final Button btn = new Button(this);
btn.setText(getString(R.string.browserRefresh));
btn.setOnClickListener(new OnClickListener()
public void onClick(View v)
dialog.cancel();
handleRefreshButton();
);
options.add(btn);
//refreshAppData (if we are on home screen)
if(this.screenData.isHomeScreen() && inglesupdated_appDelegate.rootApp.getDataURL().length() > 1)
final Button btnRefreshAppData = new Button(this);
btnRefreshAppData.setText(getString(R.string.refreshAppData));
btnRefreshAppData.setOnClickListener(new OnClickListener()
public void onClick(View v)
dialog.cancel();
refreshAppData();
);
options.add(btnRefreshAppData);
//add each option to layout, set layoutParams as we go...
for(int x = 0; x < options.size(); x++)
options.get(x).setLayoutParams(btnLayoutParams);
options.get(x).setPadding(5, 5, 5, 5);
optionsView.addView(options.get(x));
//set content view..
dialog.setContentView(optionsView);
if(options.size() > 1)
dialog.setTitle(getString(R.string.menuOptions));
else
dialog.setTitle(getString(R.string.menuNoOptions));
//show
dialog.show();
return true;
//end options menu
/////////////////////////////////////////////////////
【问题讨论】:
你是用实体手机还是模拟器? 最好使用youtube android API。在这里阅读developers.google.com/youtube/android/player 【参考方案1】:在尝试使用在 10 个 Google 搜索页面中找到的不同解决方案在 WebView 中播放 Youtube 视频 3 天后,有些解决方案有效,但并非适用于所有设备或操作系统版本,我决定以自己的方式思考。
对我来说,直接在 WebView 上播放视频的解决方案即使在最后一个 Android 设备上也会产生许多伪像:Nexus 4 因此,我必须找到一种方法来捕捉 Youtube iframe 上的点击,以便像 ios 一样直接启动 Youtube 应用程序,无需任何开发人员的努力。
public class MyWebViewClient extends WebViewClient
public Activity mActivity;
public MyWebViewClient(Activity activity)
super();
mActivity = activity;
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
Uri uri = Uri.parse(url);
if (uri.getHost().contains("youtube.com"))
IntentUtils.viewYoutube(mActivity, url);
return true;
return false;
public static void viewYoutube(Context context, String url)
IntentUtils.viewWithPackageName(context, url, "com.google.android.youtube");
public static void viewWithPackageName(Context context, String url, String packageName)
try
Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
if (isAppInstalled(context, packageName))
viewIntent.setPackage(packageName);
context.startActivity(viewIntent);
catch (Exception e)
Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
context.startActivity(viewIntent);
public static boolean isAppInstalled(Context context, String packageName)
PackageManager packageManager = context.getPackageManager();
try
packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
return true;
catch (NameNotFoundException e)
return false;
@Override
public void onPageFinished(final WebView view, String url)
String javascript = "javascript:" +
"var iframes = document.getElementsByTagName('iframe');" +
"for (var i = 0, l = iframes.length; i < l; i++) " +
" var iframe = iframes[i]," +
" a = document.createElement('a');" +
" a.setAttribute('href', iframe.src);" +
" d = document.createElement('div');" +
" d.style.width = iframe.offsetWidth + 'px';" +
" d.style.height = iframe.offsetHeight + 'px';" +
" d.style.top = iframe.offsetTop + 'px';" +
" d.style.left = iframe.offsetLeft + 'px';" +
" d.style.position = 'absolute';" +
" d.style.opacity = '0';" +
" d.style.filter = 'alpha(opacity=0)';" +
" d.style.background = 'black';" +
" a.appendChild(d);" +
" iframe.offsetParent.appendChild(a);" +
"";
view.loadUrl(javascript);
super.onPageFinished(view, url);
顺便说一句,我对为开发人员提供可接受的 WebView 所做的一些努力感到有些不安。
即使我理解希望避免仅基于 html 的 Android 应用程序,它始终是在详细信息页面上显示丰富内容的最佳/最快方式。 例如,在我的例子中,我管理一个基于 RSS 的应用程序,它允许我的客户自己更改内容。
此外,与每个数据都直接链接到 UI 组件的 Windows Phone 开发相比,我们必须花费时间将 UI 与网络数据和保存的数据链接起来。
【讨论】:
请问,我该如何使用这个类 什么是 IntentUtils?【参考方案2】:Thomas G. 的解决方案对我有用,但我必须修改 javascript。
String javascript = "javascript:" +
"setTimeout(function() " +
" var iframes = document.getElementsByTagName('iframe');" +
" for (var i = 0, l = iframes.length; i < l; i++) " +
" var iframe = iframes[i]," +
" a = document.createElement('a');" +
" a.setAttribute('href', iframe.src);" +
" d = document.createElement('div');" +
" d.style.width = iframe.offsetWidth + 'px';" +
" d.style.height = iframe.offsetHeight + 'px';" +
" d.style.top = iframe.offsetTop + 'px';" +
" d.style.left = iframe.offsetLeft + 'px';" +
" d.style.position = 'absolute';" +
" d.style.opacity = '0';" +
" d.style.filter = 'alpha(opacity=0)';" +
" d.style.zIndex='" + Integer.MAX_VALUE + "';" +
" d.style.background = 'black';" +
" a.appendChild(d);" +
" iframe.offsetParent.appendChild(a);" +
" " +
", 600);";
在 onPageFinished 事件解决了三星 Tab 设备上的一个问题之后执行 javascript,如果 js 立即执行,该问题似乎还没有准备好。我尝试了 onload 事件 / $(document).ready 但仍然没有工作。等待 600 毫秒并不理想,但我已经花了足够的时间试图让它工作。
确保 z-index 大于 iframe,最简单的方法是将 zIndex 设置为Integer.MAX_VALUE
【讨论】:
以上是关于Android webView 播放 YouTube 视频的主要内容,如果未能解决你的问题,请参考以下文章
YouTube 视频未在 WebView 中播放 - Android
如何解决在android开发中WebView在线播放视频黑屏啊?