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 视频的主要内容,如果未能解决你的问题,请参考以下文章

Android webview中如何播放网络视频

YouTube 视频未在 WebView 中播放 - Android

android 4.0 webview 无法播放视频

如何解决在android开发中WebView在线播放视频黑屏啊?

Android:在 Webview 上播放 youtube 视频

不使用 Flash 在 WebView Android 中播放 RTMP