티스토리 뷰
Stack Overflow에 자주 검색, 등록되는 문제들과 제가 개발 중 찾아 본 문제들 중에서 나중에도 찾아 볼 것 같은 문제들을 정리하고 있습니다.
Stack Overflow에서 가장 먼저 확인하게 되는 가장 높은 점수를 받은 Solution과 현 시점에 도움이 될 수 있는 가장 최근에 업데이트(최소 점수 확보)된 Solution을 각각 정리하였습니다.
아래 word cloud를 통해 이번 포스팅의 주요 키워드를 미리 확인하세요.
WebView and HTML5 <Video>
WebView 및 HTML5 <Video>
문제 내용
I'm piecing together a cheapo app that amongst other things "frames" some of our websites... Pretty simple with the WebViewClient
. until I hit the video.
우리 웹사이트 중 일부를 "프레임"으로 만드는 간단한 앱을 만들고 있습니다. WebView Client를 사용하면 매우 간단합니다. 제가 비디오를 칠 때까지.
The video is done as HTML5
elements, and these work fine and dandy on Chrome, iPhones, and now that we fixed the encoding issues it works great on Android
in the native browser.
비디오는 HTML5 요소로 수행되며, 이것들은 크롬, 아이폰에서 잘 작동하며, 이제 우리가 인코딩 문제를 해결했기 때문에 안드로이드 네이티브 브라우저에서 잘 작동합니다.
Now the rub: WebView
doesn't like it. At all. I can click on the poster image, and nothing happens.
이제 문제는: WebView는 이를 좋아하지 않습니다. 조금도... 포스터 이미지를 클릭해도 아무 일도 일어나지 않습니다.
Googling, I found this which is close, but seems to be based on a 'link' (as in a href...) instead of a video element. (onDownloadListener does not appear to get invoked on video elements...)
인터넷 검색에서 가까운 것을 찾았지만 비디오 요소 대신 '링크'(href...에서와 같이)를 기반으로 하는 것 같습니다. (onDownloadListener가 비디오 요소에서 호출되지 않는 것 같습니다...)
I also see references to overriding onShowCustomView, but that seems to not get called on video elements... nor does shouldOverrideUrlLoading..
onShowCustomView에서 오버라이딩에 대한 참조도 볼 수 있지만 비디오 요소에서는 호출되지 않는 것 같습니다. 또한 shouldOverrideUrlLoading도 마찬가지입니다..
I would rather not get into "pull xml from the server, reformat it in the app".. by keeping the story layout on the server, I can control the content a bit better without forcing people to keep updating an app. So if I can convince WebView to handle tags like the native browser, that would be best.
"서버에서 xml을 꺼내어 앱에서 다시 포맷"하지 않는 것이 좋습니다. 서버에 스토리 레이아웃을 유지함으로써, 나는 사람들이 앱을 계속 업데이트하도록 강요하지 않고 콘텐츠를 좀 더 잘 제어할 수 있습니다. 따라서 기본 브라우저와 같은 태그를 처리하도록 WebView를 설득할 수 있다면 가장 좋을 것입니다.
I'm clearly missing something obvious.. but I have no clue what.
전 분명히 뭔가를 놓치고 있는 것 같습니다. 하지만 뭐가 뭔지 전혀 모르겠어요.
높은 점수를 받은 Solution
It is possible to view a video element (video html5 tag) within a WebView, but I must say I had to deal with it for few days. These are the steps I had to follow so far:
웹뷰 내에서 비디오 요소(video html5 태그)를 볼 수 있지만 며칠 동안 처리해야 했습니다. 지금까지 제가 따라야 했던 단계는 다음과 같습니다.
- Find a properly encoded video
올바르게 인코딩된 비디오 찾기
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);
@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();
}
}
}
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();
}
But now I should say there are still an important issue. I can play it only once. The second time I click on the video dispatcher (either the poster or some play button), it does nothing.
하지만 지금 저는 여전히 중요한 문제가 있다고 말해야 합니다. 한 번만 재생 할 수 있어요. 비디오 디스패처(포스터 또는 일부 재생 버튼)를 두 번째 클릭해도 아무 것도 작동하지 않습니다.
I would also like the video to play inside the WebView frame, instead of opening the Media Player window, but this is for me a secondary issue.
Media Player(미디어 플레이어) 창을 여는 대신 WebView 프레임 안에서 비디오를 재생하고 싶지만, 이것은 저에게 부차적인 문제입니다.
가장 최근 달린 Solution
After long research, I got this thing working. See the following code:
오랜 연구 끝에 이 장치를 작동시켰습니다. 다음 코드를 참조하세요.
Test.java
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
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
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
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
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
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>
Expecting rest of things you can understand.
당신이 이해할 수 있는 나머지 것들을 기대합니다.
출처 : https://stackoverflow.com/questions/3815090/webview-and-html5-video
'개발 > 안드로이드' 카테고리의 다른 글
putExtra(), getExtra()로 다른 액티비티로 문자열 보내기 (0) | 2022.12.24 |
---|---|
안드로이드에서 클립보드에 텍스트 복사하기 (0) | 2022.12.23 |
안드로이드에서 인텐트를 사용하여 전화하기 (0) | 2022.12.23 |
Android Studio 업그레이드 후 "Default Activity Not Found" 에러 수정하기 (0) | 2022.12.23 |
'IllegalStateException: Can not perform this action after onSaveInstanceState with ViewPager' 오류 수정하기 (0) | 2022.12.22 |