WebView相关2--标题,增加header;native/H5 JS调用

本文详细介绍如何使用WebView实现native与H5交互,包括自定义WebChromeClient获取页面标题、添加HTTP头部信息、处理内部页面跳转及返回逻辑、利用JavaScriptInterface进行JS与native互相调用等关键技术。

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

网上都有,也算是差不多被人写烂了的东西,正好之前用过了,放在这儿权当个笔记记录了吧。

光一个Webview的内容,自己看起来还是有很多路要走啊。

native获取H5标题随时进行变更

比较简单,重写下onReceivedTitle就好了:

WebChromeClient wvcc = new WebChromeClient() {
    @Override
    public void onReceivedTitle(WebView view, String title) {
        super.onReceivedTitle(view, title);
        if (!TextUtils.isEmpty(title)) {
            titletext.setText(title);
        } else {
            titletext.setText("");
        }
    }

};
webview.setWebChromeClient(wvcc);
复制代码
增加给webview的访问请求增加header:
HashMap<String, String> map = new HashMap<>();
map.put("HTTP_VERSION",getVersion(this));

//加载需要显示的网页
web_push.loadUrl(url, map);复制代码

需要注意的是,如果需要进行webview的内部跳转,每次请求前是需重新添加header的,试过不重新添加,新的请求是没有的。也比较简单,(上篇文章也提到的关于再这个 shouldOverrideUrlLoading也可以进行很多相关的url进行相关拦截处理的操作。不再赘述)

     webview.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading (WebView view, String url){
            if (url.startsWith("http:www.badu")) {
                //可以进行相关操作,比如遇到XX就跳转到本地页面
                Intent xx=new Intent(this,XXXX.class);
                startActivity(xx);
                return true;
            } else if {
                //需要内部跳转的网页,添加header
                HashMap<String, String> map = new HashMap<>();
                map.put("HTTP_VERSION", MyApp.getVersion(context));
//加载需要显示的网页
                view.loadUrl(url, map);
                return super.shouldOverrideUrlLoading(view, url);
            }
        }
    });复制代码

加上之前的给请求增加cookies(cookies貌似也要给没个链接设置下),这样基础的网络请求相关的基本就算告一个小段落了。

BTW,关于内部跳转之后的退出/返回上一页,可以用

@Override
public void onBackPressed() {
    if (web_push.canGoBack()) {
        if (web_push.getUrl().equals(url)) {
//url:进入时候的初始url,放这儿也就是做个多一个判断,现在看了看,貌似加不加都行
            super.onBackPressed();
        } else {
            web_push.goBack();
        }
    } else {
        super.onBackPressed();
    }
}复制代码


进行JS操作(H5 JS调用本地native方法,本地调取JS方法获取相关信息):

也比较简单,网上也有无数现成的方法,基本都是注册个接口来进行处理,以常用的点击网页的按钮进行调用本地分享功能和本地调用JS代码获取信息进行分享两个功能来举例子

先写一个回调处理类:

public class JSInterface {
    private Activity context;
    private ShareOpen shareOpen;
    private String title="";
    private String content="";
    private String link="";
    private String img="";
    private String defLink="";

    public JSInterface(Activity context) {
        super();
        this.context = context;
    }
    /**
     * shareOpen是分享相关的方法
     */


    /**
     * JS调用Android(Java)含参数的方法
     * @param pic 图片地址
     * @param link 分享地址
     * @param tittle 分享标题
     * @param content 分享的内容
     */
    @JavascriptInterface
    public void js4Android(String pic, String link, String tittle, String content) {
        //Android代码逻辑
        shareOpen.startShare(context,pic,link,tittle+"="+content,"",false,"dream");
    }

    /**
     * 本地使用js代码之后的数据回传
     *
     * @param params 回调参数
     * @param code   回调的类型
     */
    @JavascriptInterface
    public void js4params(String params, int code) {

        if (TextUtils.isEmpty(params)) {
            YcLog.d("=======>", "params is null");
        } else {
            if (code == 1) {
                YcLog.d("=======>111", params);
                title = params;
            } else if (code == 2) {
                YcLog.d("=======>222", params);
                img = params;
            } else if (code == 3) {
                link = params;
                YcLog.d("=======>333", params);
            } else if (code == 4) {
                YcLog.d("=======>444", params);
                content = params;
            }else if(code==5){
                YcLog.d("=======>555", params);
                defLink=params;
            }else if (code==6){
                YcLog.d("=======>666", params);
                if (!TextUtils.isEmpty(params)){
                    title = params;
                }
            }
        }

        if(TextUtils.isEmpty(title)){
            title=context.getString(R.string.app_name);
        }
        if(TextUtils.isEmpty(content)){
            content=context.getString(R.string.login_slogn);
        }
        if(TextUtils.isEmpty(link)){
            link=defLink;
        }
        if(code==7){
            shareOpen.startShare(context,img,link,title+"="+content,"",false,"dream");
        }
    }

}
复制代码


Activity中webview的相关设置

Button shareButton;
      bt_share.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              web_push.post(new Runnable() {//印象中写这个是为了等待js加载完成,不写估计也行
                  @Override
                     if (Build.VERSION.SDK_INT < 19) {  
//API在19以后会有不同的调用方式,我都用的是19之前的。。。这里只是写出来表示可以作区分,
//因为个人感觉19之后的一个方法回调string回来有点局限性,对于js代码要求较高                    
                        getJs();
                      } else {
                        getJS();                 
                    } 
               });
            }
        });webview.setWebChromeClient(wvcc);//别忘记加上这个,不然js脚本就没法之行了JSInterface jsInterface = new JSInterface(context);
webview.addJavascriptInterface(jsInterface, "js4Android");//添加到webview

//需要执行的JS代码,js代码也可以是一行,也可以是js的一个复杂的函数
private void getJs() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        webview.evaluateJavascript("javascript:var a='aaa';var b='bbb'; return b;",
                new ValueCallback<String>() {
                    @Override
                    public void onReceiveValue(String value) {
                        //此处为 js 返回的结果
                        Toast.makeText(WebActivity.this, value.toString(), Toast.LENGTH_SHORT).show();
                        YcLog.d("======>", value);
                    }
                });
    }

private void getJS() {
    //获取隐藏域的相关信息,最后通过一个类型7做标志位置调用分享功能。
    webview.loadUrl("javascript:window.js4Android.js4params(location.href,5)");
    webview.loadUrl("javascript:window.js4Android.js4params(document.getElementById('shareDesc').value,4)");
    webview.loadUrl("javascript:window.js4Android.js4params(document.getElementById('shareLink').value,3)");
    webview.loadUrl("javascript:window.js4Android.js4params(document.getElementById('shareImg').value,2)");
    webview.loadUrl("javascript:window.js4Android.js4params( document.getElementById('shareTitle').value,6)");
    webview.loadUrl("javascript:window.js4Android.js4params(document.title,1)");
    webview.loadUrl("javascript:window.js4Android.js4params(000,7)");
}复制代码



帮我修复这段js代码 , 这是 uniapp 制作印章canvas 需要切换文字的字体的功能 , 需要支持小程序 h5, 字体文件都是在我的服务器上面, 字体都是 .ttf的字体 , 现在只有小程序模拟器好使 ios手机小程序好使 , 安卓系统不好使 nginx也增加过同源方案 小程序域名也配置过下载地址允许白名单内的 , 使用的方法目前是 这样的代码 ,代码里面的base64 目前路径是这样的(https://aaaa.com/xcx/font/kaiti.ttf) : // 平台环境判断 const isH5 = process.env.VUE_APP_PLATFORM === 'h5' const isMP = typeof wx !== 'undefined' && wx.canIUse('loadFontFace') // 字体缓存池 const loadedFonts = new Set() // 统一字体加载方法 export const loadFont = (fontName, base64Data) => { return new Promise((resolve, reject) => { // 检查是否已加载 if (loadedFonts.has(fontName)) { console.log(`字体 ${fontName} 已加载,使用缓存`); resolve(true); return; } // 平台判断 if (isH5) { // H5字体加载 // const style = document.createElement('style'); // style.innerHTML = `@font-face { // font-family: "${fontName}"; // src: url('${base64Data}'); // }`; // document.head.appendChild(style); // loadedFonts.add(fontName); // console.log(`H5字体 ${fontName} 加载成功`); // resolve(true); try { const style = document.createElement('style'); style.type = 'text/css'; // 显式声明样式类型:ml-citation{ref="4" data="citationList"} style.textContent = `@font-face { font-family: ${JSON.stringify(fontName)}; // 安全转义字体名 src: url('${base64Data.replace(/'/g, "\\'")}'); // 处理特殊字符 }`; document.head.appendChild(style); loadedFonts.add(fontName); resolve(true); } catch (e) { reject(new Error(`字体样式注入失败: ${e.message}`)); } } else if (isMP) { // 小程序字体加载 // wx.loadFontFace({ // family: fontName, // source: `url("${base64Data}")`, // success: () => { // loadedFonts.add(fontName); // console.log(`小程序字体 ${fontName} 加载成功`); // resolve(true); // }, // fail: (err) => { // console.error(`字体加载失败: ${fontName}`, err); // reject(new Error(`字体加载失败: ${fontName}`)); // } // }); const start = Date.now() //old // wx.loadFontFace({ // family: fontName, // source: `url("${base64Data}")`, // global: true, // success: () => { // loadedFonts.add(fontName) // console.log(`加载耗时: ${Date.now()-start}ms`) // // setTimeout(resolve(true), 300) // 增加延迟确保生效 // setTimeout(() => resolve(true), 300) // }, // fail: () => resolve(false) // }) //new uni.loadFontFace({ global: true, // 是否全局生效 family: fontName, // 定义的字体名称 source: `url("${base64Data}")`, // 字体资源的地址。建议格式为 TTF 和 WOFF,WOFF2 在低版本的iOS上会不兼容。 success() { loadedFonts.add(fontName) console.log(`加载耗时: ${Date.now()-start}ms`) // setTimeout(resolve(true), 300) // 增加延迟确保生效 setTimeout(() => resolve(true), 300) }, fail(e){ console.log(e,'失败的回调函数') }, complete(){ //console.log('接口调用结束的回调函数(调用成功、失败都会执行)') }, scopes: ["webview", "native"], }) } else { reject(new Error('不支持的平台')); } }); } // 导出常用方法 export { isH5, isMP, loadedFonts };
07-05
<template> <view class="container"> <web-view :src="webviewPath" id="targetWebview" @message="handleWebviewMessage" ></web-view> <cover-view class="bottom-action-area"> <cover-view class="capture-btn" @click="generatePDF"> <cover-view class="btn-text">立即取证</cover-view > </cover-view > </cover-view > <!-- PDF预览组件 --> <cover-view v-if="showPreview" class="pdf-preview"> <cover-view class="preview-header"> <cover-view class="title-container"> <cover-view class="preview-title">证据预览</cover-view > </cover-view > </cover-view > <web-view v-if="pdfPreviewUrl" :src="pdfPreviewUrl" class="preview-iframe" ></web-view> <cover-view class="action-buttons"> <cover-view class="action-btn cancel-btn" @click="closePreview">放弃</cover-view > <cover-view class="action-btn confirm-btn" @click="savePDF">保存证据</cover-view > </cover-view > </cover-view > </view> </template> <script setup lang="ts"> import { ref, onMounted } from 'vue'; import { onLoad, onReady } from '@dcloudio/uni-app'; import html2canvas from 'html2canvas'; // 状态管理 const webviewPath = ref(''); const showPreview = ref(false); const pdfPreviewUrl = ref(''); const pdfFilePath = ref(''); const currentWebview = ref<any>(null); const platform = ref(''); const ws = ref<any>(null); // 初始化 onLoad((options: any) => { if (options.url) { webviewPath.value = decodeURIComponent(options.url); } // 获取平台信息 const systemInfo = uni.getSystemInfoSync(); platform.value = systemInfo.platform || ''; }); // 准备阶段获取 WebView 实例 onReady(() => { // #ifdef APP-PLUS const pages = getCurrentPages(); const page = pages[pages.length - 1]; const webview = page.$getAppWebview(); setTimeout(() => { if (webview.children().length > 0) { const wv = webview.children()[0]; ws.value = wv; console.log(ws.value) } }, 1000); // #endif }); // 生成PDF const generatePDF = () => { uni.showLoading({ title: '生成证据中...', mask: true }); // #ifdef APP-PLUS // const wv = this.$scope.$getAppWebview(); setTimeout(() => { if (!ws.value) { uni.showToast({ title: '网页未加载完成', icon: 'none' }); return; } const timestamp = new Date().getTime(); const pdfPath = `_doc/evidence_${timestamp}.pdf`; // 使用 plus.draw 生成PDF ws.value.draw(ws.value, { format: 'pdf', filename: pdfPath, background: '#FFFFFF', success: (res:any) => { pdfFilePath.value = res.filePath; preparePDFPreview(res.filePath); uni.hideLoading(); }, fail: (err:any) => { console.error('PDF生成失败:', err); uni.hideLoading(); uni.showToast({ title: `生成失败: ${err.message}`, icon: 'none' }); } }); }, 800) // #endif // #ifdef H5 // H5环境使用html2canvas + jsPDF generatePDFForH5(); // #endif }; // 准备PDF预览 const preparePDFPreview = (filePath: string) => { // #ifdef APP-PLUS plus.io.resolveLocalFileSystemURL( filePath, (entry) => { const localUrl = entry.toLocalURL(); // 使用PDF.js预览 pdfPreviewUrl.value = `/static/pdfjs/web/viewer.html?file=${encodeURIComponent(localUrl)}`; showPreview.value = true; }, (err) => { console.error('文件访问失败:', err); uni.showToast({ title: '文件访问失败', icon: 'none' }); } ); // #endif }; // 保存PDF到相册 const savePDF = () => { // #ifdef APP-PLUS if (!pdfFilePath.value) return; // plus.gallery.save( // pdfFilePath.value, // { filename: `evidence_${Date.now()}.pdf` }, // () => { // uni.showToast({ title: '证据保存成功' }); // closePreview(); // }, // (err) => { // uni.showToast({ title: `保存失败: ${err.message}`, icon: 'none' }); // } // ); // #endif // #ifdef H5 // H5环境下载PDF const link = document.createElement('a'); link.href = pdfFilePath.value; link.download = `evidence_${Date.now()}.pdf`; link.click(); closePreview(); // #endif }; // 关闭预览 const closePreview = () => { showPreview.value = false; pdfPreviewUrl.value = ''; }; // H5环境PDF生成(使用html2canvas + jsPDF) const generatePDFForH5 = () => { // #ifdef H5 // const iframe = document.getElementById('targetWebview') as HTMLIFrameElement; // if (!iframe || !iframe.contentWindow) return; // const iframeDoc = iframe.contentWindow.document; // html2canvas(iframeDoc.body, { // useCORS: true, // scale: 2 // }).then(canvas => { // const imgData = canvas.toDataURL('image/jpeg', 0.95); // const pdf = new jsPDF('p', 'mm', 'a4'); // const imgWidth = pdf.internal.pageSize.getWidth(); // const imgHeight = (canvas.height * imgWidth) / canvas.width; // pdf.addImage(imgData, 'JPEG', 0, 0, imgWidth, imgHeight); // const pdfBlob = pdf.output('blob'); // pdfFilePath.value = URL.createObjectURL(pdfBlob); // // H5环境预览 // pdfPreviewUrl.value = pdfFilePath.value; // showPreview.value = true; // uni.hideLoading(); // }).catch(err => { // console.error('H5 PDF生成失败:', err); // uni.hideLoading(); // uni.showToast({ title: '生成失败', icon: 'none' }); // }); // #endif }; // 处理WebView消息 const handleWebviewMessage = (e: any) => { console.log('收到WebView消息:', e.detail); }; </script> <style scoped> .container { position: relative; height: 100vh; overflow: hidden; } #targetWebview { width: 100%; height: calc(100vh - 100px); } .bottom-action-area { position: fixed; bottom: 0; left: 0; right: 0; z-index: 100; background-color: #007aff; padding: 15px; display: flex; justify-content: center; padding-bottom: calc(10rpx + constant(safe-area-inset-bottom)); padding-bottom: calc(10rpx + env(safe-area-inset-bottom)); } .capture-btn { width: 100%; height: 90rpx; padding: 12px; background-color: #007aff; border-radius: 8px; display: flex; align-items: center; justify-content: center; } .btn-text { color: white; font-size: 14px; font-weight: 500; } /* PDF预览样式 */ .pdf-preview { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 1000; background-color: white; display: flex; flex-direction: column; } .preview-header { background-color: #007aff; padding: 15px; } .title-container { display: flex; justify-content: center; } .preview-title { color: white; font-size: 18px; font-weight: bold; } .preview-iframe { flex: 1; width: 100%; border: none; } .action-buttons { display: flex; padding: 15px; background-color: #f5f5f5; } .action-btn { flex: 1; padding: 12px; border-radius: 8px; text-align: center; font-weight: 500; } .cancel-btn { background-color: #f5f5f5; color: #333; margin-right: 10px; } .confirm-btn { background-color: #007aff; color: white; margin-left: 10px; } </style> 哪里的问题 为什么app端 webview的内容没有转换成pdf格式
最新发布
08-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值