Pergunta

Estou montando um aplicativo barato que, entre outras coisas, "enquadra" alguns de nossos sites...Muito simples com o WebViewClient.até eu clicar no vídeo.

O vídeo é feito como HTML5 elementos, e eles funcionam muito bem no Chrome, iPhones e agora que corrigimos os problemas de codificação, eles funcionam muito bem Android no navegador nativo.

Agora o problema: WebView não gosta disso.De forma alguma.Posso clicar na imagem do pôster e nada acontece.

Pesquisando no Google, encontrei esse que é próximo, mas parece ser baseado em um 'link' (como em um href...) em vez de um elemento de vídeo.(onDownloadListener não parece ser invocado em elementos de vídeo...)

Também vejo referências à substituição de onShowCustomView, mas isso parece não ser chamado em elementos de vídeo...nem deveriaOverrideUrlLoading..

Prefiro não entrar em "extrair xml do servidor, reformatá-lo no aplicativo".mantendo o layout da história no servidor, posso controlar um pouco melhor o conteúdo sem forçar as pessoas a atualizarem um aplicativo.Portanto, se eu conseguir convencer o WebView a lidar com tags como o navegador nativo, seria melhor.

Estou claramente perdendo algo óbvio ..mas não tenho ideia do quê.

Foi útil?

Solução

Eu respondo a esse tópico, caso alguém o leia e esteja interessado no resultado. É possível visualizar um elemento de vídeo (tag html5 de vídeo) dentro de um WebView, mas devo dizer que tive que lidar com isso por alguns dias. Estas são as etapas que tive que seguir até agora:

-Contar um vídeo codificado corretamente

-Ao inicializar o WebView, defina o JavaScript, plug-ins no WebViewClient e WebChromeclient.

url = new String("http://broken-links.com/tests/video/"); 
mWebView = (WebView) findViewById(R.id.webview);
mWebView.setWebChromeClient(chromeClient);
mWebView.setWebViewClient(wvClient);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setPluginState(PluginState.ON);
mWebView.loadUrl(url);

-Handle o ONSHOWCUSTOMVIEW no objeto WebChromeclient.

@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
    super.onShowCustomView(view, callback);
    if (view instanceof FrameLayout){
        FrameLayout frame = (FrameLayout) view;
        if (frame.getFocusedChild() instanceof VideoView){
            VideoView video = (VideoView) frame.getFocusedChild();
            frame.removeView(video);
            a.setContentView(video);
            video.setOnCompletionListener(this);
            video.setOnErrorListener(this);
            video.start();
        }
    }
}

-Headle o Oncompliction e os eventos do ONERROR para o vídeo, a fim de voltar à visualização da Web.

public void onCompletion(MediaPlayer mp) {
    Log.d(TAG, "Video completo");
    a.setContentView(R.layout.main);
    WebView wb = (WebView) a.findViewById(R.id.webview);
    a.initWebView();
}

Mas agora devo dizer que ainda há uma questão importante. Eu posso tocar apenas uma vez. Na segunda vez que clico no despachante de vídeo (ou em algum botão Pôster ou em alguns play), ele não faz nada.

Eu também gostaria que o vídeo seja reproduzido dentro do quadro da Webview, em vez de abrir a janela do Media Player, mas isso é para mim uma questão secundária.

Espero que ajude alguém e também agradeceria a qualquer comentário ou sugestão.

Saludos, Terrícolas.

Outras dicas

Depois de uma longa pesquisa, consegui essa coisa funcionando. Veja o seguinte código:

Test.java

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;

public class Test extends Activity {

    HTML5WebView mWebView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mWebView = new HTML5WebView(this);

        if (savedInstanceState != null) {
            mWebView.restoreState(savedInstanceState);
        } else {    
            mWebView.loadUrl("http://192.168.1.18/xxxxxxxxxxxxxxxx/");
        }

        setContentView(mWebView.getLayout());
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mWebView.saveState(outState);
    }

    @Override
    public void onStop() {
        super.onStop();
        mWebView.stopLoading();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (mWebView.inCustomView()) {
                mWebView.hideCustomView();
            //  mWebView.goBack();
                //mWebView.goBack();
                return true;
            }

        }
        return super.onKeyDown(keyCode, event);
    }
}

Html%video.java

package com.ivz.idemandtest;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.webkit.GeolocationPermissions;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;

public class HTML5WebView extends WebView {

    private Context                             mContext;
    private MyWebChromeClient                   mWebChromeClient;
    private View                                mCustomView;
    private FrameLayout                         mCustomViewContainer;
    private WebChromeClient.CustomViewCallback  mCustomViewCallback;

    private FrameLayout                         mContentView;
    private FrameLayout                         mBrowserFrameLayout;
    private FrameLayout                         mLayout;

    static final String LOGTAG = "HTML5WebView";

    private void init(Context context) {
        mContext = context;     
        Activity a = (Activity) mContext;

        mLayout = new FrameLayout(context);

        mBrowserFrameLayout = (FrameLayout) LayoutInflater.from(a).inflate(R.layout.custom_screen, null);
        mContentView = (FrameLayout) mBrowserFrameLayout.findViewById(R.id.main_content);
        mCustomViewContainer = (FrameLayout) mBrowserFrameLayout.findViewById(R.id.fullscreen_custom_content);

        mLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS);

        // Configure the webview
        WebSettings s = getSettings();
        s.setBuiltInZoomControls(true);
        s.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
        s.setUseWideViewPort(true);
        s.setLoadWithOverviewMode(true);
      //  s.setSavePassword(true);
        s.setSaveFormData(true);
        s.setJavaScriptEnabled(true);
        mWebChromeClient = new MyWebChromeClient();
        setWebChromeClient(mWebChromeClient);

        setWebViewClient(new WebViewClient());

setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);

        // enable navigator.geolocation 
       // s.setGeolocationEnabled(true);
       // s.setGeolocationDatabasePath("/data/data/org.itri.html5webview/databases/");

        // enable Web Storage: localStorage, sessionStorage
        s.setDomStorageEnabled(true);

        mContentView.addView(this);
    }

    public HTML5WebView(Context context) {
        super(context);
        init(context);
    }

    public HTML5WebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public HTML5WebView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    public FrameLayout getLayout() {
        return mLayout;
    }

    public boolean inCustomView() {
        return (mCustomView != null);
    }

    public void hideCustomView() {
        mWebChromeClient.onHideCustomView();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if ((mCustomView == null) && canGoBack()){
                goBack();
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    private class MyWebChromeClient extends WebChromeClient {
        private Bitmap      mDefaultVideoPoster;
        private View        mVideoProgressView;

        @Override
        public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback)
        {
            //Log.i(LOGTAG, "here in on ShowCustomView");
            HTML5WebView.this.setVisibility(View.GONE);

            // if a view already exists then immediately terminate the new one
            if (mCustomView != null) {
                callback.onCustomViewHidden();
                return;
            }

            mCustomViewContainer.addView(view);
            mCustomView = view;
            mCustomViewCallback = callback;
            mCustomViewContainer.setVisibility(View.VISIBLE);
        }

        @Override
        public void onHideCustomView() {
            System.out.println("customview hideeeeeeeeeeeeeeeeeeeeeeeeeee");
            if (mCustomView == null)
                return;        

            // Hide the custom view.
            mCustomView.setVisibility(View.GONE);

            // Remove the custom view from its container.
            mCustomViewContainer.removeView(mCustomView);
            mCustomView = null;
            mCustomViewContainer.setVisibility(View.GONE);
            mCustomViewCallback.onCustomViewHidden();

            HTML5WebView.this.setVisibility(View.VISIBLE);
            HTML5WebView.this.goBack();
            //Log.i(LOGTAG, "set it to webVew");
        }


        @Override
        public View getVideoLoadingProgressView() {
            //Log.i(LOGTAG, "here in on getVideoLoadingPregressView");

            if (mVideoProgressView == null) {
                LayoutInflater inflater = LayoutInflater.from(mContext);
                mVideoProgressView = inflater.inflate(R.layout.video_loading_progress, null);
            }
            return mVideoProgressView; 
        }

         @Override
         public void onReceivedTitle(WebView view, String title) {
            ((Activity) mContext).setTitle(title);
         }

         @Override
         public void onProgressChanged(WebView view, int newProgress) {
             ((Activity) mContext).getWindow().setFeatureInt(Window.FEATURE_PROGRESS, newProgress*100);
         }

         @Override
         public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
             callback.invoke(origin, true, false);
         }
    }


    static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS =
        new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}

custom_screen.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android">
    <FrameLayout android:id="@+id/fullscreen_custom_content"
        android:visibility="gone"
        android:background="@color/black"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
    />
    <LinearLayout android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout android:id="@+id/error_console"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
        />

        <FrameLayout android:id="@+id/main_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
        />
    </LinearLayout>
</FrameLayout>

video_loading_progress.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/progress_indicator"
         android:orientation="vertical"
         android:layout_centerInParent="true"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content">

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

       <TextView android:paddingTop="5dip"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_gravity="center"
           android:text="@string/loading_video" android:textSize="14sp"
           android:textColor="?android:attr/textColorPrimary" />
 </LinearLayout>

colors.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/assets/res/any/http_authentication_colors.xml
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License"); 
** you may not use this file except in compliance with the License. 
** You may obtain a copy of the License at 
**
**     http://www.apache.org/licenses/LICENSE-2.0 
**
** Unless required by applicable law or agreed to in writing, software 
** distributed under the License is distributed on an "AS IS" BASIS, 
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
** See the License for the specific language governing permissions and 
** limitations under the License.
*/
-->
<!-- FIXME: Change the name of this file!  It is now being used generically
    for the browser -->
<resources>
    <color name="username_text">#ffffffff</color>
    <color name="username_edit">#ff000000</color>

    <color name="password_text">#ffffffff</color>
    <color name="password_edit">#ff000000</color>

    <color name="ssl_text_label">#ffffffff</color>
    <color name="ssl_text_value">#ffffffff</color>

    <color name="white">#ffffffff</color>
    <color name="black">#ff000000</color>



    <color name="geolocation_permissions_prompt_background">#ffdddddd</color>
</resources>

Manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.test"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="7" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".Test"
                  android:label="@string/app_name" android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
            android:configChanges="orientation|keyboardHidden|keyboard">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>  
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_GPS" />
<uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS" />
<uses-permission android:name="android.permission.ACCESS_LOCATION" />
</manifest>

Esperando o resto das coisas que você pode entender.

mdelolmo resposta foi extremamente útil, mas, como ele disse, o vídeo só joga uma vez e, em seguida, que você não pode abri-lo novamente.

Eu olhei para este um pouco e aqui está o que eu encontrei, no caso de qualquer cansado WebView viajantes como eu tropeçar sobre este post no futuro.

Primeiro, eu olhei para o VideoView e MediaPlayerdocumentação e tenho uma ideia melhor de como os trabalhos.Eu recomendo fortemente que aqueles.

Em seguida, olhei para o código-fonte para ver como o Navegador Android não.Fazer uma página localizar e ir olhada em como lidar com eles onShowCustomView().Eles manter uma referência para o CustomViewCallbacke para o modo de exibição personalizado.

Com tudo isso, e com mdelolmo a resposta em mente, quando você é feito com o vídeo, tudo que você precisa fazer duas coisas.Em primeiro lugar, a VideoView que você salvou uma referência para chamar stopPlayback() que vai liberar a MediaPlayer para ser usado mais tarde em outro lugar.Você pode vê-lo no VideoView código-fonte.Segundo, no CustomViewCallback você salvou uma referência para chamar CustomViewCallback.onCustomViewHidden().

Depois de fazer essas duas coisas, você pode clicar sobre o mesmo vídeo ou qualquer outro vídeo e ele será aberto como antes.Não há necessidade de reiniciar todo o modo de exibição da web.

Espero que ajude.

Na verdade, parece suficiente apenas anexar um estoque webchromeclient à visualização do cliente, ala

mWebView.setWebChromeClient(new WebChromeClient());

E você precisa acelerar a aceleração de hardware!

Pelo menos, se você não precisar reproduzir um vídeo em tela cheia, não há necessidade de retirar o Videoview da WebView e empurrá-lo para a visão da atividade. Ele será reproduzido no reto alocado do elemento de vídeo.

Alguma idéia de como interceptar o botão de expansão de vídeo?

Sei que esse tópico tem vários meses, mas encontrei uma solução para reproduzir o vídeo dentro da Webview sem fazer isso em tela cheia (mas ainda assim no mídia ...). Até agora, eu não encontrei nenhuma dica sobre isso na Internet, então talvez isso também seja interessante para os outros. Ainda estou lutando em algumas questões (ou seja, colocando o media player na seção certa da tela, não sei por que estou fazendo errado, mas é um problema relativamente pequeno, eu acho ...).

No cromecliente personalizado especificar LayoutParams:

// 768x512 is the size of my video
FrameLayout.LayoutParams LayoutParameters = 
                                     new FrameLayout.LayoutParams (768, 512); 

Meu método Onshowcustomview se parece com o seguinte:

public void onShowCustomView(final View view, final CustomViewCallback callback) {
     // super.onShowCustomView(view, callback);
     if (view instanceof FrameLayout) {
         this.mCustomViewContainer = (FrameLayout) view;
         this.mCustomViewCallback = callback;
         this.mContentView = (WebView) this.kameha.findViewById(R.id.webview);
         if (this.mCustomViewContainer.getFocusedChild() instanceof VideoView) {
             this.mCustomVideoView = (VideoView) 
                                     this.mCustomViewContainer.getFocusedChild();
             this.mCustomViewContainer.setVisibility(View.VISIBLE);
             final int viewWidth = this.mContentView.getWidth();
             final int viewLeft = (viewWidth - 1024) / 2;
             // get the x-position for the video (I'm porting an iPad-Webapp to Xoom, 
             // so I can use those numbers... you have to find your own of course...
             this.LayoutParameters.leftMargin = viewLeft + 256; 
             this.LayoutParameters.topMargin = 128;
             // just add this view so the webview underneath will still be visible, 
             // but apply the LayoutParameters specified above
             this.kameha.addContentView(this.mCustomViewContainer, 
                                             this.LayoutParameters); 
             this.mCustomVideoView.setOnCompletionListener(this);
             this.mCustomVideoView.setOnErrorListener(this);
             // handle clicks on the screen (turning off the video) so you can still
             // navigate in your WebView without having the video lying over it
             this.mCustomVideoView.setOnFocusChangeListener(this); 
             this.mCustomVideoView.start();
         }
     }
 }

Então, espero poder ajudar ... eu também tive que brincar com o codificação de vídeo e vi diferentes tipos de usar o WebView com vídeo html5-no final, meu código de trabalho era uma mistura selvagem de diferentes partes que encontrei em A Internet e algumas coisas que eu tive que descobrir sozinho. Foi realmente uma dor no A*.

Essa abordagem funciona bem até 2.3 e adicionando hardwareaccelerated = true, ele mesmo funciona de 3,0 para o ICS Um problema que estou enfrentando atualmente está no segundo lançamento do aplicativo de media player está sendo travado porque não parei a reprodução e liberei o media player. Como o objeto VideoSurfaceView, que entramos na função ONSHOWCUSTOMVIEW do 3.0 OS, são específicos do navegador e não é um objeto Videoview como em, até 2.3 OS, como posso acessá -lo e stopplayback e lançar recursos?

AM é semelhante ao que o BrowerActivity faz. Para Framelayout.LayoutParams LayoutParameters = new Framelayout.LayoutParams (768, 512);

Eu acho que podemos usar

FrameLayout.LayoutParams LayoutParameters = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT,
            FrameLayout.LayoutParams.FILL_PARENT) 

em vez de.

Outra edição que conheci é se o vídeo estiver sendo reproduzido e o usuário clicar no botão Voltar, na próxima vez, você vai para esta atividade (Singletop One) e não poderá reproduzir o vídeo. Para consertar isso, liguei para o

try { 
    mCustomVideoView.stopPlayback();  
    mCustomViewCallback.onCustomViewHidden();
} catch(Throwable e) { //ignore }

no método não pressionado da atividade.

Eu sei que essa é uma pergunta muito antiga, mas você já tentou o hardwareAccelerated="true" Sinalizador manifesto para sua aplicação ou atividade?

Com esse conjunto, parece funcionar sem qualquer modificação WebChromeclient (que eu esperaria de um elemento DOM.)

eu usei html5webview Para resolver esse problema.Download e colocá -lo em seu projeto, você pode codificar exatamente assim.

private HTML5WebView mWebView;
String url = "SOMEURL";
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mWebView = new HTML5WebView(this);
    if (savedInstanceState != null) {
            mWebView.restoreState(savedInstanceState);
    } else {
            mWebView.loadUrl(url);
    }
    setContentView(mWebView.getLayout());
}
@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    mWebView.saveState(outState);
}

Para tornar o vídeo rotativo, coloque o código Android: configchanges = "orientação" em sua atividade, por exemplo (androidmanifest.xml)

<activity android:name=".ui.HTML5Activity" android:configChanges="orientation"/>

e substituir o método OnConfigurationChanged.

@Override
public void onConfigurationChanged(Configuration newConfig) {
     super.onConfigurationChanged(newConfig);
}

Essa pergunta tem anos, mas talvez minha resposta ajude pessoas como eu que precisam apoiar a versão antiga do Android. Eu tentei muitas abordagens diferentes que funcionaram em algumas versões do Android, mas não em todas. A melhor solução que encontrei é usar o Crosswalk WebView que é otimizado para o suporte ao recurso HTML5 e funciona no Android 4.1 e superior. É tão simples de usar quanto o Android WebView padrão. Você só precisa incluir a biblioteca. Aqui você pode encontrar um tutorial simples sobre como usá -lo: https://diego.org/2015/01/07/embedding-crosswalk-in-android-studio/

Bem, aparentemente isso não é possível sem usar um JNI para registrar um plugin para obter o evento de vídeo.(Pessoalmente, estou evitando JNIs, pois realmente não quero lidar com uma bagunça quando os tablets Android baseados em Atom forem lançados nos próximos meses, perdendo a portabilidade do Java.)

A única alternativa real parece ser criar uma nova página da web apenas para WebView e fazer vídeo à moda antiga com um link A HREF conforme citado no URL do Codelark acima.

Nojento.

No uso do favo de mel hardwareaccelerated=true e pluginstate.on_demand parece funcionar

Eu tive um problema parecido. Eu tinha arquivos e vídeos HTML no dobrador de ativos do meu aplicativo.

Portanto, os vídeos estavam localizados dentro do APK. Como o APK é realmente um zip-arquivo, o WebView não conseguiu ler os arquivos de vídeo.

Copiar todos os arquivos de html e vídeo para o cartão SD funcionou para mim.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top