欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Qt在Android平台上实现html转PDF的功能

程序员文章站 2022-04-09 14:58:46
Qt for Android Qt for Android enables you to run Qt 5 applications Android devices. All Qt modules (essential and add-on) are supported except Qt WebE ......

qt for android

qt for android enables you to run qt 5 applications android devices. all qt modules (essential and add-on) are supported except qt webengine, qt serial port, and the platform-specific ones (qt mac extras, qt windows extras, and qt x11 extras).

 

在windows或者linux平台上可以用qtwebengine模块实现网页预览和打印成pdf文档,用起来很方便,生成的文档质量也比较好,但在android平台上qtwebengine模块不能用,想要显示网页可以用qtwebview模块,不支持打印成pdf。尝试用qtextdocument和qprinter将html转为pdf,发现qtextdocument不支持css样式,生成的pdf文档排版是错的。

 

查看qtwebview在android平台上的实现,可以发现其用的就是android的webview控件实现的网页显示。尝试在android平台上实现html生成pdf,找到了这篇文章,验证后可行。需要依赖第三方库dexmaker,可以用谷歌实现的 implementation 'com.google.dexmaker:dexmaker:1.2',库文件名为dexmaker-1.2.jar。

 

修改qt源码,在android平台上实现html转pdf的功能

  • 修改$qtsrc/qtwebview/src/jar/src/org/qtproject/qt5/android/view/qtandroidwebviewcontroller.java文件
    /****************************************************************************
    **
    ** copyright (c) 2015 the qt company ltd.
    ** contact: http://www.qt.io/licensing/
    **
    ** this file is part of the qtwebview module of the qt toolkit.
    **
    ** $qt_begin_license:lgpl3$
    ** commercial license usage
    ** licensees holding valid commercial qt licenses may use this file in
    ** accordance with the commercial license agreement provided with the
    ** software or, alternatively, in accordance with the terms contained in
    ** a written agreement between you and the qt company. for licensing terms
    ** and conditions see http://www.qt.io/terms-conditions. for further
    ** information use the contact form at http://www.qt.io/contact-us.
    **
    ** gnu lesser general public license usage
    ** alternatively, this file may be used under the terms of the gnu lesser
    ** general public license version 3 as published by the free software
    ** foundation and appearing in the file license.lgplv3 included in the
    ** packaging of this file. please review the following information to
    ** ensure the gnu lesser general public license version 3 requirements
    ** will be met: https://www.gnu.org/licenses/lgpl.html.
    **
    ** gnu general public license usage
    ** alternatively, this file may be used under the terms of the gnu
    ** general public license version 2.0 or later as published by the free
    ** software foundation and appearing in the file license.gpl included in
    ** the packaging of this file. please review the following information to
    ** ensure the gnu general public license version 2.0 requirements will be
    ** met: http://www.gnu.org/licenses/gpl-2.0.html.
    **
    ** $qt_end_license$
    **
    ****************************************************************************/
    
    package org.qtproject.qt5.android.view;
    
    import android.content.pm.packagemanager;
    import android.view.view;
    import android.webkit.geolocationpermissions;
    import android.webkit.urlutil;
    import android.webkit.valuecallback;
    import android.annotation.suppresslint;
    import android.content.context;
    import android.os.bundle;
    import android.os.cancellationsignal;
    import android.os.parcelfiledescriptor;
    import android.print.pagerange;
    import android.print.printattributes;
    import android.print.printdocumentadapter;
    import android.webkit.webview;
    import android.webkit.webviewclient;
    import android.webkit.webchromeclient;
    
    import java.lang.runnable;
    
    import android.app.activity;
    import android.content.intent;
    import android.net.uri;
    
    import java.lang.string;
    
    import android.webkit.websettings;
    import android.webkit.websettings.pluginstate;
    import android.graphics.bitmap;
    
    import java.util.concurrent.semaphore;
    import java.io.file;
    import java.io.ioexception;
    import java.lang.reflect.invocationhandler;
    import java.lang.reflect.method;
    
    import android.os.build;
    
    import java.util.concurrent.timeunit;
    
    import com.google.dexmaker.stock.proxybuilder;
    
    public class qtandroidwebviewcontroller
    {
        private final activity m_activity;
        private final long m_id;
        private boolean busy;
        private boolean m_haslocationpermission;
        private webview m_webview = null;
        private static final string tag = "qtandroidwebviewcontroller";
        private final int init_state = 0;
        private final int started_state = 1;
        private final int loading_state = 2;
        private final int finished_state = 3;
    
        private volatile int m_loadingstate = init_state;
        private volatile int m_progress = 0;
        private volatile int m_framecount = 0;
    
        // api 11 methods
        private method m_webviewonresume = null;
        private method m_webviewonpause = null;
        private method m_websettingssetdisplayzoomcontrols = null;
    
        // api 19 methods
        private method m_webviewevaluatejavascript = null;
    
        // native callbacks
        private native void c_onpagefinished(long id, string url);
        private native void c_onpagestarted(long id, string url, bitmap icon);
        private native void c_onprogresschanged(long id, int newprogress);
        private native void c_onreceivedicon(long id, bitmap icon);
        private native void c_onreceivedtitle(long id, string title);
        private native void c_onrunjavascriptresult(long id, long callbackid, string result);
        private native void c_onreceivederror(long id, int errorcode, string description, string url);
        private native void c_onpdfprintingfinished(long id, boolean succeed);
    
        // we need to block the ui thread in some cases, if it takes to long we should timeout before
        // anr kicks in... usually the hard limit is set to 10s and if exceed that then we're in trouble.
        // in general we should not let input events be delayed for more then 500ms (if we're spending more
        // then 200ms somethings off...).
        private final long blocking_timeout = 250;
    
        private void resetloadingstate(final int state)
        {
            m_progress = 0;
            m_framecount = 0;
            m_loadingstate = state;
        }
    
        private class html2pdf {
            private file file;
            private file dexcachefile;
            private printdocumentadapter printadapter;
            private pagerange[] ranges;
            private parcelfiledescriptor descriptor;
        
            private void printtopdf(webview webview, string filename) {
                if (webview != null) {
                    file = new file(filename);
                    dexcachefile = webview.getcontext().getdir("dex", 0);
                    if (!dexcachefile.exists()) {
                        dexcachefile.mkdir();
                    }
                    try {
                        if (file.exists()) {
                            file.delete();
                        }
                        file.createnewfile();
                        descriptor = parcelfiledescriptor.open(file, parcelfiledescriptor.mode_read_write);
                        printattributes attributes = new printattributes.builder()
                                .setmediasize(printattributes.mediasize.iso_a4)
                                .setresolution(new printattributes.resolution("id", context.print_service, 300, 300))
                                .setcolormode(printattributes.color_mode_color)
                                .setminmargins(printattributes.margins.no_margins)
                                .build();
                        ranges = new pagerange[]{pagerange.all_pages};
        
                        printadapter = webview.createprintdocumentadapter();
                        printadapter.onstart();
                        printadapter.onlayout(attributes, attributes, new cancellationsignal(), getlayoutresultcallback(new invocationhandler() {
                            @override
                            public object invoke(object proxy, method method, object[] args) throws throwable {
                                if (method.getname().equals("onlayoutfinished")) {
                                    onlayoutsuccess();
                                } else {
                                    descriptor.close();
                                    c_onpdfprintingfinished(m_id, false);
                                    busy = false;
                                }
                                return null;
                            }
                        }, dexcachefile.getabsolutefile()), new bundle());
                    } catch (ioexception e) {
                        if (descriptor != null) {
                            try {
                                descriptor.close();
                            } catch (ioexception ex) {
                                ex.printstacktrace();
                            }
                        }
                        c_onpdfprintingfinished(m_id, false);
                        e.printstacktrace();
                        busy = false;
                    }
                }
            }
        
            private void onlayoutsuccess() throws ioexception {
                printdocumentadapter.writeresultcallback callback = getwriteresultcallback(new invocationhandler() {
                    @override
                    public object invoke(object o, method method, object[] objects) throws throwable {
                        if (method.getname().equals("onwritefinished")) {
                            c_onpdfprintingfinished(m_id, true);
                        } else {
                            c_onpdfprintingfinished(m_id, false);
                        }
                        busy = false;
                        if (descriptor != null) {
                            try {
                                descriptor.close();
                            } catch (ioexception ex) {
                                ex.printstacktrace();
                            }
                        }
                        return null;
                    }
                }, dexcachefile.getabsolutefile());
                printadapter.onwrite(ranges, descriptor, new cancellationsignal(), callback);
            }
        
            @suppresslint("newapi")
            private  printdocumentadapter.layoutresultcallback getlayoutresultcallback(invocationhandler invocationhandler, file dexcachedir) throws ioexception {
                return proxybuilder.forclass(printdocumentadapter.layoutresultcallback.class)
                        .dexcache(dexcachedir)
                        .handler(invocationhandler)
                        .build();
            }
        
            @suppresslint("newapi")
            private  printdocumentadapter.writeresultcallback getwriteresultcallback(invocationhandler invocationhandler, file dexcachedir) throws ioexception {
                return proxybuilder.forclass(printdocumentadapter.writeresultcallback.class)
                        .dexcache(dexcachedir)
                        .handler(invocationhandler)
                        .build();
            }    
        }
    
        private class qtandroidwebviewclient extends webviewclient
        {
            qtandroidwebviewclient() { super(); }
    
            @override
            public boolean shouldoverrideurlloading(webview view, string url)
            {
                // handle http: and http:, etc., as usual
                if (urlutil.isvalidurl(url))
                    return false;
    
                // try to handle geo:, tel:, mailto: and other schemes
                try {
                    intent intent = new intent(intent.action_view, uri.parse(url));
                    view.getcontext().startactivity(intent);
                    return true;
                } catch (exception e) {
                    e.printstacktrace();
                }
    
                return false;
            }
    
            @override
            public void onloadresource(webview view, string url)
            {
                super.onloadresource(view, url);
            }
    
            @override
            public void onpagefinished(webview view, string url)
            {
                super.onpagefinished(view, url);
                m_loadingstate = finished_state;
                if (m_progress != 100) // onprogresschanged() will notify qt if we didn't finish here.
                    return;
    
                 m_framecount = 0;
                 c_onpagefinished(m_id, url);
            }
    
            @override
            public void onpagestarted(webview view, string url, bitmap favicon)
            {
                super.onpagestarted(view, url, favicon);
                if (++m_framecount == 1) { // only call onpagestarted for the first frame.
                    m_loadingstate = loading_state;
                    c_onpagestarted(m_id, url, favicon);
                }
            }
    
            @override
            public void onreceivederror(webview view,
                                        int errorcode,
                                        string description,
                                        string url)
            {
                super.onreceivederror(view, errorcode, description, url);
                resetloadingstate(init_state);
                c_onreceivederror(m_id, errorcode, description, url);
            }
        }
    
        private class qtandroidwebchromeclient extends webchromeclient
        {
            qtandroidwebchromeclient() { super(); }
            @override
            public void onprogresschanged(webview view, int newprogress)
            {
                super.onprogresschanged(view, newprogress);
                m_progress = newprogress;
                c_onprogresschanged(m_id, newprogress);
                if (m_loadingstate == finished_state && m_progress == 100) { // did we finish?
                    m_framecount = 0;
                    c_onpagefinished(m_id, view.geturl());
                }
            }
    
            @override
            public void onreceivedicon(webview view, bitmap icon)
            {
                super.onreceivedicon(view, icon);
                c_onreceivedicon(m_id, icon);
            }
    
            @override
            public void onreceivedtitle(webview view, string title)
            {
                super.onreceivedtitle(view, title);
                c_onreceivedtitle(m_id, title);
            }
    
            @override
            public void ongeolocationpermissionsshowprompt(string origin, geolocationpermissions.callback callback)
            {
                callback.invoke(origin, m_haslocationpermission, false);
            }
        }
    
        public qtandroidwebviewcontroller(final activity activity, final long id)
        {
            m_activity = activity;
            m_id = id;
            final semaphore sem = new semaphore(0);
            m_activity.runonuithread(new runnable() {
                @override
                public void run() {
                    m_webview = new webview(m_activity);
                    m_haslocationpermission = haslocationpermission(m_webview);
                    websettings websettings = m_webview.getsettings();
    
                    if (build.version.sdk_int > 10) {
                        try {
                            m_webviewonresume = m_webview.getclass().getmethod("onresume");
                            m_webviewonpause = m_webview.getclass().getmethod("onpause");
                            m_websettingssetdisplayzoomcontrols = websettings.getclass().getmethod("setdisplayzoomcontrols", boolean.class);
                            if (build.version.sdk_int > 18) {
                                m_webviewevaluatejavascript = m_webview.getclass().getmethod("evaluatejavascript",
                                                                                             string.class,
                                                                                             valuecallback.class);
                            }
                        } catch (exception e) { /* do nothing */ e.printstacktrace(); }
                    }
    
                    //allowing access to location without actual access_fine_location may throw security exception
                    websettings.setgeolocationenabled(m_haslocationpermission);
    
                    websettings.setjavascriptenabled(true);
                    if (m_websettingssetdisplayzoomcontrols != null) {
                        try { m_websettingssetdisplayzoomcontrols.invoke(websettings, false); } catch (exception e) { e.printstacktrace(); }
                    }
                    websettings.setbuiltinzoomcontrols(true);
                    websettings.setpluginstate(pluginstate.on);
                    m_webview.setwebviewclient((webviewclient)new qtandroidwebviewclient());
                    m_webview.setwebchromeclient((webchromeclient)new qtandroidwebchromeclient());
                    sem.release();
                }
            });
    
            try {
                sem.acquire();
            } catch (exception e) {
                e.printstacktrace();
            }
        }
    
        public void loadurl(final string url)
        {
            if (url == null) {
                return;
            }
    
            resetloadingstate(started_state);
            c_onpagestarted(m_id, url, null);
            m_activity.runonuithread(new runnable() {
                @override
                public void run() { m_webview.loadurl(url); }
            });
        }
    
        public void loaddata(final string data, final string mimetype, final string encoding)
        {
            if (data == null)
                return;
    
            resetloadingstate(started_state);
            c_onpagestarted(m_id, null, null);
            m_activity.runonuithread(new runnable() {
                @override
                public void run() { m_webview.loaddata(data, mimetype, encoding); }
            });
        }
    
        public void loaddatawithbaseurl(final string baseurl,
                                        final string data,
                                        final string mimetype,
                                        final string encoding,
                                        final string historyurl)
        {
            if (data == null)
                return;
    
            resetloadingstate(started_state);
            c_onpagestarted(m_id, null, null);
            m_activity.runonuithread(new runnable() {
                @override
                public void run() { m_webview.loaddatawithbaseurl(baseurl, data, mimetype, encoding, historyurl); }
            });
        }
    
        public void goback()
        {
            m_activity.runonuithread(new runnable() {
                @override
                public void run() { m_webview.goback(); }
            });
        }
    
        public boolean cangoback()
        {
            final boolean[] back = {false};
            final semaphore sem = new semaphore(0);
            m_activity.runonuithread(new runnable() {
                @override
                public void run() { back[0] = m_webview.cangoback(); sem.release(); }
            });
    
            try {
                sem.tryacquire(blocking_timeout, timeunit.milliseconds);
            } catch (exception e) {
                e.printstacktrace();
            }
    
            return back[0];
        }
    
        public void goforward()
        {
            m_activity.runonuithread(new runnable() {
                @override
                public void run() { m_webview.goforward(); }
            });
        }
    
        public boolean cangoforward()
        {
            final boolean[] forward = {false};
            final semaphore sem = new semaphore(0);
            m_activity.runonuithread(new runnable() {
                @override
                public void run() { forward[0] = m_webview.cangoforward(); sem.release(); }
            });
    
            try {
                sem.tryacquire(blocking_timeout, timeunit.milliseconds);
            } catch (exception e) {
                e.printstacktrace();
            }
    
            return forward[0];
        }
    
        public void stoploading()
        {
            m_activity.runonuithread(new runnable() {
                @override
                public void run() { m_webview.stoploading(); }
            });
        }
    
        public void reload()
        {
            m_activity.runonuithread(new runnable() {
                @override
                public void run() { m_webview.reload(); }
            });
        }
    
        public string gettitle()
        {
            final string[] title = {""};
            final semaphore sem = new semaphore(0);
            m_activity.runonuithread(new runnable() {
                @override
                public void run() { title[0] = m_webview.gettitle(); sem.release(); }
            });
    
            try {
                sem.tryacquire(blocking_timeout, timeunit.milliseconds);
            } catch (exception e) {
                e.printstacktrace();
            }
    
            return title[0];
        }
    
        public int getprogress()
        {
            return m_progress;
        }
    
        public boolean isloading()
        {
            return m_loadingstate == loading_state || m_loadingstate == started_state || (m_progress > 0 && m_progress < 100);
        }
    
        public void runjavascript(final string script, final long callbackid)
        {
            if (script == null)
                return;
    
            if (build.version.sdk_int < 19 || m_webviewevaluatejavascript == null)
                return;
    
            m_activity.runonuithread(new runnable() {
                @override
                public void run() {
                    try {
                        m_webviewevaluatejavascript.invoke(m_webview, script, callbackid == -1 ? null :
                            new valuecallback<string>() {
                                @override
                                public void onreceivevalue(string result) {
                                    c_onrunjavascriptresult(m_id, callbackid, result);
                                }
                            });
                    } catch (exception e) {
                        e.printstacktrace();
                    }
                }
            });
        }
    
        public string geturl()
        {
            final string[] url = {""};
            final semaphore sem = new semaphore(0);
            m_activity.runonuithread(new runnable() {
                @override
                public void run() { url[0] = m_webview.geturl(); sem.release(); }
            });
    
            try {
                sem.tryacquire(blocking_timeout, timeunit.milliseconds);
            } catch (exception e) {
                e.printstacktrace();
            }
    
            return url[0];
        }
    
        public webview getwebview()
        {
           return m_webview;
        }
    
        public void onpause()
        {
            if (m_webviewonpause == null)
                return;
    
            m_activity.runonuithread(new runnable() {
                @override
                public void run() { try { m_webviewonpause.invoke(m_webview); } catch (exception e) { e.printstacktrace(); } }
            });
        }
    
        public void onresume()
        {
            if (m_webviewonresume == null)
                return;
    
            m_activity.runonuithread(new runnable() {
                @override
                public void run() { try { m_webviewonresume.invoke(m_webview); } catch (exception e) { e.printstacktrace(); } }
            });
        }
    
        private static boolean haslocationpermission(view view)
        {
            final string name = view.getcontext().getpackagename();
            final packagemanager pm = view.getcontext().getpackagemanager();
            return pm.checkpermission("android.permission.access_fine_location", name) == packagemanager.permission_granted;
        }
    
        public void destroy()
        {
            m_activity.runonuithread(new runnable() {
                @override
                public void run() {
                    m_webview.destroy();
                }
            });
        }
    
        public void printtopdf(final string filename){
            if(!busy){
                busy = true;
                m_activity.runonuithread(new runnable() {
                    @override
                    public void run() { 
                        html2pdf html2pdf = new html2pdf();
                        html2pdf.printtopdf(m_webview, filename);
                    }
                });
            }else{
                c_onpdfprintingfinished(m_id,false); 
            }
        }
    
    }

     

 

  1. 主要修改:
    1. 增加了 void printtopdf(final string filename)打印接口
    2. 增加了 native void c_onpdfprintingfinished(long id, boolean succeed)作为打印完成的回调
    3. 增加了内部类html2pdf实现打印成pdf
  • 修改$qtsrc/qtwebview/src/plugins/android/qandroidwebview_p.h
  1. 增加槽函数 void printtopdf(const qstring &filename) q_decl_override;
  • 修改$qtsrc/qtwebview/src/plugins/android/qandroidwebview.cpp
  1. 实现槽函数
    void qandroidwebviewprivate::printtopdf(const qstring &filename)
    {
        const qjniobjectprivate &filenamestring = qjniobjectprivate::fromstring(filename);
        m_viewcontroller.callmethod<void>("printtopdf","(ljava/lang/string;)v",filenamestring.object());
    }
  2. 实现java代码中打印完成的回调
    static void c_onpdfprintingfinished(jnienv *env,
                                  jobject thiz,
                                  jlong id,
                                  jboolean succeed)
    {
        q_unused(env)
        q_unused(thiz)
        const webviews &wv = (*g_webviews);
        qandroidwebviewprivate *wc = wv[id];
        if (!wc)
            return;
        q_emit wc->pdfprintingfinished(succeed);
    }
  3. 修改jniexport jint jni_onload(javavm* vm, void* /*reserved*/),注册c_onpdfprintingfinished回调函数。
    jninativemethod methods[]数组里增加一项
    {"c_onpdfprintingfinished","(jz)v",reinterpret_cast<void *>(c_onpdfprintingfinished)}

 

  • 修改$qtsrc/qtwebview/src/webview/qabstractwebview_p.h(以下增加的所有的c++代码、函数、信号等都用#if android宏条件编译)
  1. 增加信号void pdfprintingfinished(bool succeed);
  • 修改$qtsrc/qtwebview/src/webview/qquickwebview_p.h
  1. 增加公开槽函数 void printtopdf(const qstring &filename) q_decl_override;
  2. 增加信号 void pdfprintingfinished(bool succeed);
  3. 增加私有槽函数 void onpdfprintingfinished(bool succeed);
  • 修改$qtsrc/qtwebview/src/webview/qquickwebview.cpp
  1. 构造函数里关联槽函数和信号
    #if android
    connect(m_webview, &qwebview::pdfprintingfinished, this, &qquickwebview::onpdfprintingfinished);
    #endif
  2. 实现槽函数printtopdf
    #if android
    void qquickwebview::printtopdf(const qstring &filename)
    {
        m_webview->printtopdf(filename);
    }
    #endif
  3. 实现槽函数onpdfprintingfinished
    #if android
    void qquickwebview::onpdfprintingfinished(bool succeed)
    {
        q_emit pdfprintingfinished(succeed);
    }
    #endif
  • 修改$qtsrc/qtwebview/src/webview/qwebviewinterface_p.h
  1. 增加纯虚函数 virtual void printtopdf(const qstring &filename) = 0;
  • 修改$qtsrc/qtwebview/src/webview/qwebviewfactory.cpp
  1. qnullwebview类增加
    void printtopdf(const qstring &filename) override
        {q_unused(filename); }
  • 修改$qtsrc/qtwebview/src/webview/qwebview_p.h
  1. 增加公开槽函数 void printtopdf(const qstring &filename) q_decl_override;
  2. 增加信号 void pdfprintingfinished(bool succeed);
  3. 增加私有槽函数 void onpdfprintingfinished(bool succeed);
  • 修改$qtsrc/qtwebview/src/webview/qwebview.cpp
  1. 构造函数里关联槽函数和信号
    #if android
    connect(d, &qabstractwebview::pdfprintingfinished, this, &qwebview::onpdfprintingfinished);
    #endif
  2. 实现槽函数printtopdf
    #if android
    void qwebview::printtopdf(const qstring &filename)
    {
        d->printtopdf(filename);
    }
    #endif
  3. 实现槽函数onpdfprintingfinished
    #if android
    void qwebview::onpdfprintingfinished(bool succeed)
    {
        q_emit pdfprintingfinished(succeed);
    }
    #endif
  • 在$qtsrc/qtwebview/src/jar目录下新建lib目录,将dexmaker-1.2.jar文件拷贝到该目录下
  • 修改$qtsrc/qtwebview/src/jar/jar.pro
    target = qtandroidwebview
    
    load(qt_build_paths)
    config += java
    destdir = $$module_base_outdir/jar
    
    javaclasspath += $$pwd/src \
        $$pwd/lib/dexmaker-1.2.jar
    
    javasources += $$pwd/src/org/qtproject/qt5/android/view/qtandroidwebviewcontroller.java
    
    # install
    target.path = $$[qt_install_prefix]/jar
    target.files += $$pwd/lib/dexmaker-1.2.jar
    installs += target
  • 修改$qtsrc/qtwebview/src/webview/webview.pro
    ……
    
    qmake_docs = \
                 $$pwd/doc/qtwebview.qdocconf

    android_bundled_jar_dependencies = \
        jar/qtandroidwebview.jar \
        jar/dexmaker-1.2.jar
    android_permissions = \
        android.permission.access_fine_location
    android_lib_dependencies = \
        plugins/webview/libqtwebview_android.so

    headers += $$public_headers $$private_headers

    load(qt_module)
  • 修改$qtsrc/qtwebview/src/imports/plugins.qmltypes
  1. 增加信号
    signal {
                name: "pdfprintingfinished"
                revision: 1
                parameter { name: "succeed"; type: "bool" }
            }
  2. 增加方法
    method {
                name: "printtopdf"
                revision: 1
                parameter { name: "filename"; type: "string" }
            }
  • 配置和编译

  1. ./configure -extprefix $qtinstall/android_arm64_v8a -xplatform android-clang -release -nomake tests -nomake examples -opensource -confirm-license -recheck-all -android-ndk $ndkpath -android-sdk $androidsdkpath -android-ndk-host linux-x86_64 -android-arch arm64-v8a
    -android-arch支持armeabi, armeabi-v7a, arm64-v8a, x86, x86_64,一次只能编译一个架构,注意不同架构要修改安装目录
  2. make -j8
  3. make install
  • 上述软件版本
  1. qt:5.13.2
  2. ndk:r20b(20.1.5948944)
  3. android-buildtoolsversion:29.0.2
  • 使用示例
    import qtquick 2.12
    import qtquick.window 2.12
    import qtwebview 1.1
    import qtquick.controls 2.12
    
    window {
        id: window
        visible: true
        width: 1080
        height: 1920
        title: qstr("hello world")
        webview{
            id:webview
            anchors.bottom: printbtn.top
            anchors.right: parent.right
            anchors.left: parent.left
            anchors.top: parent.top
            anchors.bottommargin: 0
            url:"http://www.qq.com"
            onpdfprintingfinished: {
                printbtn.text = "打印" + (succeed?"成功":"失败")
                printbtn.enabled = true
            }
        }
    
        button {
            id:printbtn
            text: "打印"
            anchors.bottom: parent.bottom
            anchors.bottommargin: 0
            onclicked: {
                printbtn.enabled = false
                webview.printtopdf("/sdcard/aaa.pdf")
            }
        }
    }

下期预告:在android平台上实现串口读写的功能