Webview and HTML5 <video>

本文详细介绍了如何在Android的WebView中正确播放HTML5视频。通过设置WebViewClient和WebChromeClient,可以实现视频的正常播放,并解决了视频仅能播放一次的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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.

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.

Now the rub: WebView doesn't like it. At all. I can click on the poster image, and nothing happens.

Googling, I found http://www.codelark.com/2010/05/12/android-viewing-video-from-embedded-webview/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...)

I also see references to overriding onShowCustomView, but that seems to not get called on video elements... nor does 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.

I'm clearly missing something obvious.. but I have no clue what.

share | improve this question
 
feedback

12 Answers

up vote 38 down vote accepted

I answer this topic just in case someone read it and is interested on the result. 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:

-Find a properly encoded video

-When initializing the WebView, set the JavaScript, Plug-ins the WebViewClient and the 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().setPluginsEnabled(true);
mWebView.loadUrl(url);

-Handle the onShowCustomView in the WebChromeClient object.

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

-Handle the onCompletion and the onError events for the video, in order to get back to the web view.

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.

I hope it helps somebody, and I would also thank any comment or suggestion.

Saludos, terrícolas.

share | improve this answer
 
i donot run any result,my screen is black. i want to know if this need thead. my code is too long to posy here.can you give me some contact way or open a another topic. –  pengwang  Oct 8 '10 at 2:59
 
Are you completely sure is not an encoding issue? Try with the URL I posted. Obviously it has to work with the native browser –  mdelolmo  Oct 8 '10 at 14:49
 
thanks, mdelolmo! that got my onShowCustomView to work... will have to play with it. Note that the emulator (at least on Linux) doesn't like playing videos much at all, even things a real android will play fine. And encoding is a bit of a pain... took a while to get things set up here so that the droids would stream. –  brian moore  Oct 8 '10 at 22:44
 
Thanks mdelolmo! I tried to figure out the issue with it only playing once and I wrote it up in an answer to this question. –  littleFluffyKitty  Jan 9 '11 at 4:49
9  
what is "a"? like that activity would that be? –  Collin Price  Mar 8 at 3:21
show 2 more comments
feedback

mdelolmo's answer was incredibly helpful, but like he said, the video only plays once and then you can't open it again.

I looked into this a bit and here is what I found, in case any weary WebView travelers like myself stumble on this post in the future.

First off, I looked at the VideoView and MediaPlayer's documentation and got a better sense of how those work. I strongly recommend those.

Then, I looked at the source code to see how the Android Browser does it. Do a page find and go look at how they handle onShowCustomView(). They keep a reference to the CustomViewCallbackand to the custom view.

With all of that, and with mdelolmo's answer in mind, when you are done with the video, all you need to do is two things. First, on the VideoView that you saved a reference to, call stopPlayback() that will release the MediaPlayer to be used later elsewhere. You can see it in the VideoView source code. Second, on the CustomViewCallback you saved a reference to callCustomViewCallback.onCustomViewHidden().

After doing those two things, you can click on the same video or any other video and it will open like before. No need to restart the entire WebView.

Hope that helps.

share | improve this answer
 
Actually I kinda solved it too, I just didn't remember to post the solution (shame on me). Just like you, I had to have a look to Android browser's code, and well, not much to add, I used the onCompletion listener to stop the Player instead and had to set visibility's a couple of times, but I would buy your solution. Regards! –  mdelolmo Jan 10 '11 at 7:57
 
Google Code Search has been retired, I think this version of BrowserActivity.java (from Froyo) roughly corresponds to the one described here: github.com/android/platform_packages_apps_browser/blob/… –  spacemanaki  Jan 24 at 19:12
 
@spacemanki's linked code doesn't contain any mentions of VideoView in the whole repo. I guess BrowserActivity does it differently now. I even went back to the Eclair release. Can't find any usage. –  Max Howell  Mar 22 at 18:56
feedback

I know this thread is several months old, but I found a solution for playing the video inside the WebView without doing it fullscreen (but still in the media player...). So far, I didn't find any hint on this in the internet so maybe this is also interesting for others. I'm still struggling on some issues (i.e. placing the media player in the right section of the screen, don't know why I'm doing it wrong but it's a relatively small issue I think...).

In the Custom ChromeClient specify LayoutParams:

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

My onShowCustomView method looks like this:

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

So, I hope I could help... I too had to play around with video-Encoding and saw different kinds of using the WebView with html5 video - in the end my working code was a wild mix of different code-parts I found in the internet and some things I had to figure out by myself. It really was a pain in the a*.

share | improve this answer
 
feedback

After long research, I got this thing working. See the following code:

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>

Expecting rest of things you can understand.

share | improve this answer
 
feedback

A-M's is similar to what the BrowerActivity does. for FrameLayout.LayoutParams LayoutParameters = new FrameLayout.LayoutParams (768, 512);

I think we can use

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

instead.

Another issue I met is if the video is playing, and user clicks the back button, next time, you go to this activity(singleTop one) and can not play the video. to fix this, I called the

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

in the activity's onBackPressed method.

share | improve this answer
 
feedback

Actually, it seems sufficient to merely attach a stock WebChromeClient to the client view, ala

mWebView.setWebChromeClient(new WebChromeClient());

At least, if you don't need to play a full-screen video, there's no need to pull the VideoView out of the WebView and push it into the Activity's view. It will play in the video element's allotted rect.

Any ideas how to intercept the expand video button?

share | improve this answer
 
Didn't work for me. –  Max Howell  Mar 22 at 18:59
 
Worked here, thanks! –  Paulo Cesar  Jun 22 at 19:16
feedback

This approach works well very till 2.3 And by adding hardwareaccelerated=true it even works from 3.0 to ICS One problem i am facing currently is upon second launch of media player application is getting crashed because i have not stopped playback and released Media player. As VideoSurfaceView object, which we get in onShowCustomView function from 3.0 OS, are specific to browser and not a VideoView object as in, till 2.3 OS How can i access it and stopPlayback and release resources?

share | improve this answer
 
feedback

I used html5webview to solve this problem.Download and put it into your project then you can code just like this.

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

To make the video rotatable, put android:configChanges="orientation" code into your Activity for example (Androidmanifest.xml)

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

and override the onConfigurationChanged method.

@Override
public void onConfigurationChanged(Configuration newConfig) {
     super.onConfigurationChanged(newConfig);
}
share | improve this answer
 
feedback

Well, apparently this is just not possible without using a JNI to register a plugin to get the video event. (Personally, I am avoiding JNI's since I really don't want to deal with a mess when Atom-based android tablets come out in the next few months, losing the portability of Java.)

The only real alternative seems to be to create a new web page just for WebView and do video the old-school way with an A HREF link as cited in the Codelark url above.

Icky.

share | improve this answer
 
feedback

I know this is an very old question, but have you tried the hardwareAccelerated="true" manifest flag for your application or activity?

With this set, it seems to work without any WebChromeClient modification (which I would expect from an DOM-Element.)

share | improve this answer
 
1  
hardwareAccelerated starts from Android 3.0 –  vbence  Jan 4 at 19:56
feedback

On honeycomb use hardwareaccelerated=true and pluginstate.on_demand seems to work

share | improve this answer
 
feedback

@littleFluffyKitty

According to your code, playing videos works fine but when the video is paused and if we move to home screen or we use any other application and if we return back to our video app, the video view is not retained. The video is played from beginning.

The Log cat says,

couldn't save which view has focus because the focused view android.widget.VideoView@4057df18 has no id 

What am I missing ? can you throw me some light ?

share | improve this answer
 
Was this post useful to you?      


原文链接: http://stackoverflow.com/questions/3815090/webview-and-html5-video/10366253#10366253
<template> <view class="content"> <button @click="selectFile()">打开安卓文件管理器</button> <view> <text>单选URL</text> {{ filePath }} </view> <view><text>多选</text></view> <view v-for="(item, index) in fileList">{{ item }}</view> </view> </template> <script> export default { data() { return { filePath: '', fileList: [] }; }, onLoad() {}, methods: { selectFile() { let that = this; let main = plus.android.runtimeMainActivity(); let Intent = plus.android.importClass('android.content.Intent'); let intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType('*/*'); intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); //关键!多选参数 intent.addCategory(Intent.CATEGORY_OPENABLE); main.startActivityForResult(intent, 200); // 获取回调 main.onActivityResult = function(requestCode, resultCode, data) { let Activity = plus.android.importClass('android.app.Activity'); let ContentUris = plus.android.importClass('android.content.ContentUris'); let Cursor = plus.android.importClass('android.database.Cursor'); let Uri = plus.android.importClass('android.net.Uri'); let Build = plus.android.importClass('android.os.Build'); let Environment = plus.android.importClass('android.os.Environment'); let DocumentsContract = plus.android.importClass('android.provider.DocumentsContract'); // 给系统导入 contentResolver let contentResolver = main.getContentResolver(); plus.android.importClass(contentResolver); // 返回路径 let path = ''; if (resultCode == Activity.RESULT_OK) { if (data.getData() != null) { let uri = data.getData(); //this 当前mainClass实例 path = getPath(this, uri); that.filePath = path; } else { try { let ClipData = plus.android.importClass('android.content.ClipData'); let clipData = new ClipData(); clipData = data.getClipData(); if (clipData != null) { for (let i = 0; i < clipData.getItemCount(); i++) { let item = clipData.getItemAt(i); let uri = item.getUri(); let url = getPath(this, uri); that.fileList.push(url); } } } catch (e) { console.log(e); } } console.log(path); } else { console.log(path); } function getPath(context, uri) { try { let isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider //一些三方的文件浏览器会进入到这个方法中,例如ES //QQ文件管理器不在此列 if (isExternalStorageDocument(uri)) { let docId = DocumentsContract.getDocumentId(uri); let split = docId.split(':'); let type = split[0]; // 如果是手机内部存储 if ('primary' == type.toLowerCase()) { return Environment.getExternalStorageDirectory() + '/' + split[1]; } else { return '/storage/' + type + '/' + split[1]; } } // DownloadsProvider else if (isDownloadsDocument(uri)) { let id = DocumentsContract.getDocumentId(uri); if (id.indexOf('raw:') > -1) { id = id.replace('raw:', ''); } // 不同系统获取的id开头可能不一样,在这后面便是真实的地址 if (id.substring(0, 5) == 'msf:') { return id.substring(5, id.length); } let contentUri = ContentUris.withAppendedId(Uri.parse('content://downloads/public_downloads'), ContentUris.parseId(uri)); return getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { let MediaStore = plus.android.importClass('android.provider.MediaStore'); let docId = DocumentsContract.getDocumentId(uri); let split = docId.split(':'); let type = split[0]; let contentUri = null; if ('image' == type.toLowerCase()) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ('video' == type.toLowerCase()) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ('audio' == type.toLowerCase()) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } let selection = '_id=?'; let selectionArgs = [split[1]]; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ('content' == uri.getScheme().toLowerCase()) { if (isGooglePhotosUri(uri)) { return uri.getLastPathSegment(); } else if (isQQMediaDocument(uri)) { let paths = uri.getPath(); let Environment = plus.android.importClass('android.os.Environment'); let fileDir = Environment.getExternalStorageDirectory(); let files = plus.android.importClass('java.io.File'); let file = new files(fileDir, paths.substring('/QQBrowser'.length, paths.length)); return file.getPath(); } return getDataColumn(context, uri, null, null); } // File else if ('file' == uri.getScheme().toLowerCase()) { return uri.getPath(); } return null; } catch (e) { console.log(e); return null; } } // 通过uri 查找出绝对路径 function getDataColumn(context, uri, selection, selectionArgs) { try { let MediaStore = plus.android.importClass('android.provider.MediaStore'); let cursor = null; let column = MediaStore.MediaColumns.DATA; let projection = [column]; cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { let column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } } catch (e) { console.log(e); return null; } } function isExternalStorageDocument(uri) { return 'com.android.externalstorage.documents' == uri.getAuthority() ? true : false; } function isDownloadsDocument(uri) { return 'com.android.providers.downloads.documents' == uri.getAuthority() ? true : false; } function isMediaDocument(uri) { return 'com.android.providers.media.documents' == uri.getAuthority() ? true : false; } /** * 使用第三方qq文件管理器打开 * * @param uri * @return */ function isQQMediaDocument(uri) { return 'com.tencent.mtt.fileprovider' == uri.getAuthority() ? true : false; } /** * @param uri The Uri to check. * @return Whether the Uri authority is Google Photos. */ function isGooglePhotosUri(uri) { return 'com.google.android.apps.photos.content' == uri.getAuthority() ? true : false; } }; } } }; </script> <style></style> 我现在需要读取选择的.bin文件,并且读到里边的内容和文件的大小,并可以将文件里边的二进制文件打印出来,手机app实现,请参考以下两个路径下中的方法进行更改https://www.html5plus.org/doc/zh_cn/io.html文件和https://www.html5plus.org/doc/zh_cn/android.html文件
05-14
QGridLayout * villageLayout = new QGridLayout(); // 创建 QWebEngineView 实例 QWebEngineView* webView = new QWebEngineView(parent); // 全面启用WebEngine功能 webView->settings()->setAttribute(QWebEngineSettings::AutoLoadImages, true); webView->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); webView->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); webView->settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true); webView->settings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, false); webView->settings()->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, true); webView->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true); webView->settings()->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, true); webView->settings()->setAttribute(QWebEngineSettings::LinksIncludedInFocusChain, true); // 原始信息保持不变 QString info = "<h1><img src=\"http://127.0.0.1:8090/api/v1/public/system/exam/get-temp?path=/uploads/web/contents/202508/08/324653451ede48ac8eeb573e7f4d840c.png\" alt=\"\" data-href=\"\" style=\"\"/>\uD83E\uDD23\uD83D\uDE0C\uD83D\uDE04 <a href=\"https://www.baidu.com/\" target=\"_blank\">百度</a> </h1><table style=\"width: auto;\"><tbody><tr><th colSpan=\"1\" rowSpan=\"1\" width=\"auto\">1</th><th colSpan=\"1\" rowSpan=\"1\" width=\"auto\">2</th><th colSpan=\"1\" rowSpan=\"1\" width=\"auto\">3</th></tr><tr><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">4</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">5</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">6</td></tr></tbody></table><h3 style=\"line-height: 1.5;\"><span style=\"color: rgb(255, 77, 79); font-family: "Times New Roman";\">签收单</span></h3><ul><li>1</li><li>2</li></ul><ol><li>3</li><li>4</li></ol><div data-w-e-type=\"todo\"><input type=\"checkbox\" disabled >1212</div><pre><code > webView->settings()->setAttribute(QWebEngineSettings::AutoLoadImages, true);</code></pre><p><br></p><hr/><p><br></p><div data-w-e-type=\"video\" data-w-e-is-void>\n<video poster=\"\" controls=\"true\" width=\"auto\" height=\"auto\"><source src=\"http://127.0.0.1:8090/api/v1/public/system/exam/get-temp?path=/uploads/web/contents/202508/11/f6b2d6301cd34b6db1a03841a3b33000.mp4\" type=\"video/mp4\"/></video>\n</div><p><br></p>"; // 创建完整的HTML文档 QString styledInfo = "<!DOCTYPE html>" "<html>" "<head>" "<meta charset=\"UTF-8\">" "<style>" "table { border-collapse: collapse !important; border: 1px solid black !important; width: auto; }" "th, td { border: 1px solid black !important; padding: 8px !important; text-align: left; }" "th { background-color: #f2f2f2 !important; font-weight: bold; }" "ul, ol { margin: 10px 0; padding-left: 20px; }" "li { margin: 5px 0; }" "video { max-width: 100%; height: auto; border: 1px solid #ccc; display: block; margin: 10px 0; }" "pre { background-color: #f5f5f5; padding: 10px; border-radius: 4px; overflow-x: auto; }" "hr { margin: 15px 0; border: 0; border-top: 1px solid #eee; }" "</style>" "</head>" "<body>" + info + "</body>" "</html>"; webView->setHtml(styledInfo, QUrl("http://127.0.0.1:8090/")); villageLayout->addWidget(webView); ui->widgetImage->setLayout(villageLayout); \uD83E\uDD23\uD83D\uDE0C\uD83D\uDE04 qt无法识别此编码
最新发布
08-12
在使用 `QWebEngineView` 的 `setHtml` 方法显示 HTML 内容时,若遇到编码识别问题,通常表现为网页中的文本出现乱码或显示异常。为了解决此类问题,可以从以下几个方面入手: ### 设置 HTML 内容的编码格式 在调用 `setHtml` 时,可以通过指定内容的编码格式来确保 `QWebEngineView` 正确解析网页内容。如果 HTML 内容本身未明确声明字符集,`QWebEngineView` 可能会使用默认的编码方式(通常是 UTF-8),这可能导致与实际内容编码不符的情况。 可以通过在 HTML 内容中显式添加 `<meta charset="UTF-8">` 或 `<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">` 来声明字符集,以确保浏览器引擎能够正确识别编码格式。此外,在调用 `setHtml` 时,还可以通过 `QUrl` 指定编码: ```cpp QString htmlContent = "<html><head><meta charset=\"UTF-8\"></head><body>你好,世界!</body></html>"; QUrl baseUrl = QUrl::fromLocalFile("/path/to/local/dir/"); webView->setHtml(htmlContent, baseUrl); ``` ### 使用 QTextCodec 设置默认编码 如果 HTML 内容使用的是非 UTF-8 编码(如 GBK、ISO-8859-1 等),可以在程序启动时设置 `QTextCodec` 为本地编码,以确保 `QWebEngineView` 能够正确解析这些内容: ```cpp #include <QTextCodec> int main(int argc, char *argv[]) { QApplication app(argc, argv); QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK")); // 或其他编码格式 QWebEngineView *webView = new QWebEngineView(); QString htmlContent = "你好,世界!"; // 假设此内容为 GBK 编码 webView->setHtml(htmlContent); webView->show(); return app.exec(); } ``` ### 使用 QWebEngineSettings 设置默认文本编码 `QWebEngineView` 提供了 `QWebEngineSettings` 类,可以用于设置网页的默认文本编码格式。这在加载本地 HTML 文件或网络资源时特别有用: ```cpp QWebEngineSettings *settings = webView->settings(); settings->setDefaultTextEncoding("UTF-8"); // 设置默认编码为 UTF-8 ``` ### 处理外部资源加载时的编码问题 当 `QWebEngineView` 加载外部资源(如通过 `QWebEnginePage::setHtml` 加载的本地 HTML 文件)时,若文件本身使用非 UTF-8 编码保存,需要确保文件在读取时被正确转换为 UTF-8。可以使用 `QFile` 和 `QTextStream` 读取文件,并在读取时指定编码: ```cpp QFile file(":/path/to/file.html"); if (file.open(QIODevice::ReadOnly)) { QTextStream stream(&file); stream.setCodec("GBK"); // 根据文件实际编码设置 QString htmlContent = stream.readAll(); file.close(); webView->setHtml(htmlContent); } ``` ### 相关问题 1. 如何在 Qt 中使用 QWebEngineSettings 设置网页的默认字体和编码? 2. QWebEngineView setHtml 方法在加载本地资源时需要注意哪些事项? 3. 如何确保 QWebEngineView 在加载非 UTF-8 编码网页时正确显示中文? 4. 使用 QTextStream 读取非 UTF-8 编码文件时如何避免乱码? 5. QWebEngineView 在不同操作系统上对编码的支持是否有差异? [^1]: QWebEngineView 控件由于使用了 OpenGL,在某些电脑上可能由于 OpenGL 的驱动过低会导致花屏或者各种奇奇怪怪的问题,比如 `showFullScreen` 的情况下鼠标右键失效,需要在 `main` 函数启用软件 OpenGL 渲染。 [^2]: 验证安装:要验证 WebEngine 模块是否正确安装,可以尝试创建一个简单的 Qt 应用程序,并将其集成到 WebEngine 中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值