Подтвердить что ты не робот

WebView и HTML5 <video>

Я собираю приложение cheapo, которое, помимо прочего, "кадрирует" некоторые из наших веб-сайтов... Довольно просто с WebViewClient. пока я не ударил видео.

Видео выполняется как элементы HTML5, и они отлично работают и денди в Chrome, iPhone, и теперь, когда мы исправили проблемы с кодировкой, он отлично работает на Android в собственном браузере.

Теперь rub: WebView не нравится. Вообще. Я могу нажать на изображение плаката, и ничего не происходит.

Googling, я нашел этот, который близок, но, похоже, основан на ссылке (как в href... ) вместо видеоэлемента. (onDownloadListener не появляется для вызова на видеоэлементы...)

Я также вижу ссылки на переопределение onShowCustomView, но это, похоже, не вызывает вызовы на видеоэлементы... и не должно делатьOverrideUrlLoading..

Я бы предпочел не "втягивать xml с сервера, переформатировать его в приложении".., сохраняя макет истории на сервере, я могу немного контролировать контент, не заставляя людей продолжать обновлять приложение. Поэтому, если я могу убедить WebView обрабатывать теги, такие как собственный браузер, это было бы лучше.

Мне явно не хватает чего-то очевидного.. но я понятия не имею, что.

4b9b3361

Ответ 1

Я отвечаю на эту тему на всякий случай, если кто-то ее прочитает и интересуется результатом. В WebView можно просмотреть видеоэлемент (тег видео html5), но я должен сказать, что мне приходилось иметь дело с ним в течение нескольких дней. Это те шаги, которые я должен был выполнить до сих пор:

-Настроить правильно закодированное видео

-При инициализации WebView установите JavaScript, Подключаемые модули WebViewClient и 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);

-Установить onShowCustomView в объекте 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();
        }
    }
}

-Установите onCompletion и события onError для видео, чтобы вернуться к веб-просмотру.

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();
}

Но теперь я должен сказать, что все еще есть важная проблема. Я могу сыграть его только один раз. Во второй раз я нажимаю на диспетчера видео (либо плакат, либо кнопка воспроизведения), он ничего не делает.

Мне также хотелось бы, чтобы видео воспроизводилось внутри фрейма WebView вместо открытия окна Media Player, но это для меня вторичная проблема.

Я надеюсь, что это поможет кому-то, и я также хотел бы поблагодарить любые комментарии или предложения.

Салудос, терриколас.

Ответ 2

После долгих исследований я получил эту работу. См. Следующий код:

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>

Ожидая остальное, что вы можете понять.

Ответ 3

Ответ mdelolmo был невероятно полезен, но, как он сказал, видео воспроизводится только один раз, а затем вы не можете его снова открыть.

Я немного заглянул в это, и вот что я нашел, в случае, если любые усталые путешественники WebView, такие как я, наткнутся на этот пост в будущем.

Во-первых, я посмотрел VideoView и MediaPlayer и получили лучшее представление о том, как они работают. Я настоятельно рекомендую их.

Затем я просмотрел исходный код , чтобы увидеть, как это делает браузер Android. Найдите страницу и посмотрите, как они обрабатывают onShowCustomView(). Они сохраняют ссылку на CustomViewCallback и на пользовательский вид.

Со всем этим, и с учетом mdelolmo, когда вы закончите с видео, все, что вам нужно сделать, это две вещи. Во-первых, на VideoView, на который вы сохранили ссылку, вызовите stopPlayback(), который выпустит MediaPlayer, который будет использоваться позже в другом месте. Вы можете увидеть его в исходном коде VideoView. Во-вторых, на CustomViewCallback вы сохранили ссылку на вызов CustomViewCallback.onCustomViewHidden().

После выполнения этих двух действий вы можете нажать на одно и то же видео или любое другое видео, и оно будет открыто, как раньше. Нет необходимости перезапускать весь WebView.

Надеюсь, что это поможет.

Ответ 4

На самом деле, кажется достаточным просто прикрепить веб-браузер WebChromeClient к представлению клиента, ala

mWebView.setWebChromeClient(new WebChromeClient());

и вам нужно включить аппаратное ускорение!

По крайней мере, если вам не нужно воспроизводить полноэкранное видео, нет необходимости вытаскивать VideoView из WebView и вставлять его в представление Activity. Он будет воспроизводиться в элементе видео, выделенном rect.

Любые идеи, как перехватить кнопку расширения видео?

Ответ 5

Я знаю, что этой теме несколько месяцев, но я нашел решение для воспроизведения видео в WebView, не делая его полноэкранным (но все же в медиаплеере...). До сих пор я не нашел в этом намека на это, так что, возможно, это тоже интересно для других. Я по-прежнему борюсь за некоторые проблемы (например, размещая медиа-плеер в правой части экрана, не знаю, почему я делаю это неправильно, но это относительно небольшая проблема, о которой я думаю...).

В пользовательском ChromeClient укажите LayoutParams:

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

Мой метод onShowCustomView выглядит следующим образом:

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();
         }
     }
 }

Итак, я надеюсь, что смогу помочь... Мне тоже пришлось поиграть с кодированием видео и увидеть различные виды использования WebView с видео html5 - в конце мой рабочий код был диким сочетанием разных кодовых частей Я нашел в Интернете и некоторые вещи, которые мне приходилось решать самостоятельно. Это действительно было болью в *.

Ответ 6

Этот подход хорошо работает до 2.3 И добавив hardwareaccelerated = true, он даже работает от 3.0 до ICS Одна из проблем, с которой я столкнулся в настоящее время, - это второй запуск приложения медиаплеера, который разбился, потому что я не остановил воспроизведение и выпустил медиаплеер. Поскольку объект VideoSurfaceView, который мы получаем в функции onShowCustomView из 3.0 ОС, специфичен для браузера, а не для объекта VideoView, как и до 2.3 ОС Как я могу получить к нему доступ и остановитьPlayback и освободить ресурсы?

Ответ 7

A-M похож на то, что делает BrowerActivity. для     FrameLayout.LayoutParams LayoutParameters = новый FrameLayout.LayoutParams(768, 512);

Я думаю, мы можем использовать

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

вместо.

Другая проблема, с которой я столкнулся, - это воспроизведение видео, и пользователь нажимает кнопку "Назад", в следующий раз вы переходите к этой операции (singleTop one) и не можете воспроизводить видео. Чтобы исправить это, я назвал

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

в операции onBackPressed.

Ответ 8

Я знаю, что это очень старый вопрос, но вы пробовали флаг манифеста hardwareAccelerated="true" для своего приложения или активности?

С этим набором он работает без какой-либо модификации WebChromeClient (чего я ожидал бы от DOM-Element.)

Ответ 9

Я использовал html5webview, чтобы решить эту проблему. Загрузите и поместите его в свой проект, тогда вы можете закодировать именно так.

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);
}

Чтобы сделать видеоролик поворотным, поместите в свою активность android: configChanges = "ориентация" например (Androidmanifest.xml)

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

и переопределить метод onConfigurationChanged.

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

Ответ 10

Этот вопрос лет, но, возможно, мой ответ поможет таким людям, как я, которые должны поддерживать старую версию Android. Я пробовал много разных подходов, которые работали на некоторых версиях Android, но не на всех. Лучшее решение, которое я нашел, это использовать Crosswalk Webview, который оптимизирован для поддержки функций HTML5 и работает на Android 4.1 и выше. Он так же прост в использовании, как Android WebView по умолчанию. Вам просто нужно включить библиотеку. Здесь вы можете найти простой учебник по его использованию: https://diego.org/2015/01/07/embedding-crosswalk-in-android-studio/

Ответ 11

При использовании сотовой связи hardwareaccelerated=true и pluginstate.on_demand, похоже, работает

Ответ 12

У меня была аналогичная проблема. У меня были файлы HTML и видео в папке с ресурсами моего приложения.

Поэтому видео были расположены внутри APK. Поскольку APK - действительно ZIP файл, WebView не смог прочитать видеофайлы.

Копирование всех HTML- и видеофайлов на SD-карту работало для меня.

Ответ 13

Ну, по-видимому, это просто невозможно без использования JNI для регистрации плагина, чтобы получить видео-событие. (Лично я избегаю JNI, так как я действительно не хочу разбираться с беспорядком, когда в ближайшие несколько месяцев выходят Atom-планшетные Android-приложения, теряющие мобильность Java.)

Единственная реальная альтернатива, похоже, заключается в создании новой веб-страницы только для WebView и записи видео по старой школе с помощью ссылки HREF, указанной в URL-адресе Codelark выше.

Icky.