Android之WebView加载PDF链接预览PDF文件及加载富文本

文章介绍了如何在Android应用中使用WebView加载并预览PDF文件,避免使用大型第三方库。主要步骤包括在assets目录下存放JavaScript文件和HTML,设置WebView的WebSettings,通过JavaScript解析并渲染PDF。这种方法使得内库保持小巧,适合处理PDF的简单预览需求。

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


前言

Android的webview压根就不支持加载pdf,Android与iOS不同,iOS加载pdf,不管本地还是在线,直接使用webview渲染就可以了,而Android却做不到,所以我们必须得扣脑壳了。方法也有很多种,比如第三方PDFview,MuPDF等,但是不推荐,引入进去apk体积会大很多,所以大多场景都是通过js解析,然后在webview中加载PDF文件,所以内库很小也就2兆多,那我们就开始吧。


一、效果图

在这里插入图片描述

二、实现步骤

1.在项目main目录下新建一个assets

2.新建一个js为index.js

var url = location.search.substring(1);

PDFJS.cMapUrl = 'https://unpkg.com/pdfjs-dist@1.9.426/cmaps/';
PDFJS.cMapPacked = true;

var pdfDoc = null;

function createPage() {
    var div = document.createElement("canvas");
    document.body.appendChild(div);
    return div;
}

function renderPage(num) {
    pdfDoc.getPage(num).then(function (page) {
        var viewport = page.getViewport(2.0);
        var canvas = createPage();
        var ctx = canvas.getContext('2d');

        canvas.height = viewport.height;
        canvas.width = viewport.width;

        page.render({
            canvasContext: ctx,
            viewport: viewport
        });
    });
}

PDFJS.getDocument(url).then(function (pdf) {
    pdfDoc = pdf;
    for (var i = 1; i <= pdfDoc.numPages; i++) {
        renderPage(i)
    }
});


3.新建一个HTML为index.html

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=4.0,user-scalable=no"/>
    <title>Document</title>
    <style type="text/css">
        canvas {
            width: 100%;
            height: 100%;
            border: 1px solid black;
        }
    </style>
    <script src="https://unpkg.com/pdfjs-dist@1.9.426/build/pdf.min.js"></script>
    <script type="text/javascript" src="index.js"></script>
</head>
<body>
</body>
</html>


4.xml布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff">

    <include
        android:id="@+id/include"
        layout="@layout/title_layout" />

    <WebView
        android:id="@+id/pdfwebview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/gosign"
        android:layout_below="@+id/include" />

    <TextView
        android:id="@+id/gosign"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_alignParentBottom="true"
        android:layout_centerInParent="true"
        android:layout_marginLeft="30dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="30dp"
        android:layout_marginBottom="30dp"
        android:background="@drawable/blackground_home_blue"
        android:gravity="center"
        android:text="Go Sign"
        android:textColor="#ffffff"
        android:textSize="16dp" />

</RelativeLayout>

4.Activity类(kotlin)

注意:activity代码要点在于WebSettings设置的参数和loadUrl()加载URL时加"file:///android_asset/index.html?"

class PDFWebViewActivity : Activity(), OnClickListener {

    private lateinit var relative_back: RelativeLayout
    private lateinit var text_title: TextView
    private lateinit var url: String//接收URL
    private lateinit var title: String//接收title
    private lateinit var pdfwebview: WebView
    private lateinit var gosign: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //去掉状态栏
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            val decorView = window.decorView
            val option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            decorView.systemUiVisibility = option
            window.statusBarColor = Color.parseColor("#00000000")
        }
        //修改状态栏文字为黑色
        window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
                View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
        setContentView(R.layout.pdfwebviewlayout)
        url = intent.getStringExtra("url")!!.trim { it <= ' ' }
        title = intent.getStringExtra("title")!!.trim { it <= ' ' }
        instantiation()
    }

    fun instantiation() {
        EventBus.getDefault().register(this)
        relative_back = findViewById(R.id.relative_back)
        text_title = findViewById(R.id.text_title)
        text_title.text = title
        pdfwebview = findViewById(R.id.pdfwebview)
        gosign = findViewById(R.id.gosign)
        gosign.setOnClickListener(this)
        relative_back.setOnClickListener(this)
        webviewDe(url)
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.relative_back ->
                finish()
        }
    }

    /**
     * webview显示
     */
    @SuppressLint("SetJavaScriptEnabled", "JavascriptInterface")
    private fun webviewDe(url: String) {
        println("网页url打印:$url")
        val webSettings: WebSettings = pdfwebview.getSettings()
        webSettings.cacheMode = WebSettings.LOAD_DEFAULT
        //设置加载进来的页面自适应手机屏幕
        webSettings.useWideViewPort = true
        webSettings.loadWithOverviewMode = true
        //问题2:基本都需要支持JS
        webSettings.javaScriptEnabled = true
        webSettings.allowFileAccess = true
        //支持通过JS打开新窗口
        webSettings.allowFileAccessFromFileURLs = true
        webSettings.allowUniversalAccessFromFileURLs = true
        webSettings.javaScriptCanOpenWindowsAutomatically = true
        webSettings.setGeolocationEnabled(true)
        webSettings.domStorageEnabled = true
        webSettings.setAppCacheEnabled(false)
        pdfwebview.scrollBarStyle = WebView.SCROLLBARS_OUTSIDE_OVERLAY
        //触摸焦点起作用
        pdfwebview.requestFocus()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            //http与https的方法
            webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
        }
        // 添加js交互接口类,并起别名 Android
        pdfwebview.addJavascriptInterface(JavascriptImgInterface(), "Android")

        //设置WebChromeClient类
        pdfwebview.webChromeClient = object : WebChromeClient() {
            //获取网站标题
            override fun onReceivedTitle(view: WebView, title: String) {
                println("标题在这里$title")
            }

            //图片选取
            override fun onShowFileChooser(
                webView: WebView,
                filePathCallback: ValueCallback<Array<Uri>>,
                fileChooserParams: FileChooserParams,
            ): Boolean {
                return true
            }
        }
        //设置WebViewClient类
        pdfwebview.webViewClient = object : WebViewClient() {
            override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
                // 返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器
                view.loadUrl(url)
                return true
            }

            //设置加载前的函数 kotlin这里favicon: Bitmap?的Bitmap?必须加?
            override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
                if (!this@PDFWebViewActivity.isFinishing) 
                {
                    println("开始加载了")
                    DialogUtils.showLoadingDialog(this@PDFWebViewActivity)
                }
            }

            //设置结束加载函数
            override fun onPageFinished(view: WebView, url: String) {
                println("结束加载了")
                try {
                    DialogUtils.hideLoadingDialog()
                } catch (e: Exception) {
                }
            }
        }
        pdfwebview.loadUrl("file:///android_asset/index.html?$url")
    }

    class JavascriptImgInterface {
        /**
         * 注意: 在Android4.2极其以上系统需要给提供js调用的方法前加入一个注释:@JavaScriptInterface;
         * 在虚拟机当中 Javascript调用Java方法会检测这个anotation,
         * 如果方法被标识@JavaScriptInterface则Javascript可以成功调用这个Java方法,否则调用不成功。
         */
        //js调用Android方法给web返回Token值
        @JavascriptInterface
        open fun Android_Token(): String? {
            println("打印的token:" + SpUtil.get(ConstantUtil.TOKEN, ""))
            return SpUtil.get(ConstantUtil.TOKEN, "")
        }

    }
}

5.Activity类(Java)

注意:activity代码要点在于WebSettings设置的参数和loadUrl()加载URL时加"file:///android_asset/index.html?"


public class PDFWebViewActivity extends Activity implements View.OnClickListener {

    private String url;//接收URL
    private String title;//接收title
    private ImageView left_black_risk;//返回键
    private TextView text_title;//title
    private RelativeLayout gosign;//去签名
    private static String message;//返回消息
    private WebView webviewx5;//webview

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //去掉状态栏
        if (Build.VERSION.SDK_INT >= 21) {
            View decorView = getWindow().getDecorView();
            int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            decorView.setSystemUiVisibility(option);
            getWindow().setStatusBarColor(Color.parseColor("#00000000"));
        }
        //修改状态栏文字为黑色
        getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
                        View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        setContentView(R.layout.pdfwebviewlayout);
        url = getIntent().getStringExtra("url").trim();
        title = getIntent().getStringExtra("title").trim();
        instantiation();
    }

    private void instantiation() {
        webviewx5 = findViewById(R.id.webview);
        left_black_risk = findViewById(R.id.left_black_risk);
        text_title = findViewById(R.id.text_title);
        gosign = findViewById(R.id.gosign);
        text_title.setText(title);
        left_black_risk.setOnClickListener(this);
        gosign.setOnClickListener(this);
        webviewDe(url);
    }

    /**
     * webview显示
     */
    @SuppressLint({"SetJavaScriptEnabled", "JavascriptInterface"})
    private void webviewDe(String url) {
        System.out.println("网页url打印:" + url);
        WebSettings webSettings = webviewx5.getSettings();
        webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
        //设置加载进来的页面自适应手机屏幕
        webSettings.setUseWideViewPort(true);
        webSettings.setLoadWithOverviewMode(true);
        //问题2:基本都需要支持JS
        webSettings.setJavaScriptEnabled(true);
        webSettings.setAllowFileAccess(true);
        //支持通过JS打开新窗口
        webSettings.setAllowFileAccessFromFileURLs(true);
        webSettings.setAllowUniversalAccessFromFileURLs(true);
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        webSettings.setGeolocationEnabled(true);
        webSettings.setDomStorageEnabled(true);
        webSettings.setAppCacheEnabled(false);

        webviewx5.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
        //触摸焦点起作用
        webviewx5.requestFocus();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            //http与https的方法
            webSettings.setMixedContentMode(MIXED_CONTENT_ALWAYS_ALLOW);
        }
        // 添加js交互接口类,并起别名 Android
        webviewx5.addJavascriptInterface(new JavascriptImgInterface(), "Android");

        //设置WebChromeClient类
        webviewx5.setWebChromeClient(new WebChromeClient() {


            //获取网站标题
            @Override
            public void onReceivedTitle(WebView view, String title) {
                System.out.println("标题在这里" + title);
            }

            //图片选取
            @Override
            public boolean onShowFileChooser(WebView webView,
                                             ValueCallback<Uri[]> filePathCallback,
                                             FileChooserParams fileChooserParams) {
                return true;
            }
        });
        //设置WebViewClient类
        webviewx5.setWebViewClient(new WebViewClient() {

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                // 返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器
                view.loadUrl(url);
                return true;
            }

            //设置加载前的函数
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                if (!PDFWebViewActivity.this.isFinishing())//xActivity即为本界面的Activity
                {
                    System.out.println("开始加载了");
                    DialogUtils.showLoadingDialog(PDFWebViewActivity.this);
                }
            }

            //设置结束加载函数
            @Override
            public void onPageFinished(WebView view, String url) {
                System.out.println("结束加载了");
                try {
                    DialogUtils.hideLoadingDialog();
                } catch (Exception e) {

                }
            }
        });
        webviewx5.loadUrl("file:///android_asset/index.html?" + url);
    }

    public class JavascriptImgInterface {
        /**
         * 注意: 在Android4.2极其以上系统需要给提供js调用的方法前加入一个注释:@JavaScriptInterface;
         * 在虚拟机当中 Javascript调用Java方法会检测这个anotation,
         * 如果方法被标识@JavaScriptInterface则Javascript可以成功调用这个Java方法,否则调用不成功。
         */
        //js调用Android方法给web返回Token值
        @JavascriptInterface
        public String Android_Token() {
            System.out.println("打印的token:" + SpUtil.get(ConstantUtil.TOKEN, ""));
            return SpUtil.get(ConstantUtil.TOKEN, "");
        }

        //js调用Android方法下载apk
        @JavascriptInterface
        public String Android_DownloadApk(String url) {
            //方式一:代码实现跳转
            Intent intent = new Intent();
            //通过intent发送数据
            intent.setAction("android.intent.action.VIEW");
            //创建一个链接  链接.语法解析
            Uri content_url = Uri.parse(url);
            //通过intent接受
            intent.setData(content_url);
            //开始于当前intent
            startActivity(intent);
            return url;
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            //返回键
            case R.id.left_black_risk:
                finish();
                break;
            //去签名
            case R.id.gosign:
                break;
        }
    }
}


三、WebView加载富文本

1.添加依赖,新建一个xml文件

     //用于加载富文本
    implementation 'org.jsoup:jsoup:1.14.1'
  <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/text_logon"
        android:layout_margin="15dp"
        android:focusable="false"
        android:scrollbars="none"
        android:nestedScrollingEnabled="false"
        android:layout_below="@+id/included"/>

2.activity界面

 private lateinit var webview: WebView
    private var CSS_STYLE =
        " <style> p {color:#FFFFFF;} a {color:#FFFFFF;}  img{max-width:100% !important;height:auto !important}video{max-width:100% !important;height:auto !important}  p span{color:#FFFFFF !important; background-color:#1A1A1A !important; font-size:20px;}</style>"
    private var CSS_Content =
        "<h3><span style=\"color: rgb(245, 245, 245);\">hello world</span></h3><p><span style=\"color: rgb(255, 251, 143);\">阿大声道啊大师大的撒打算的撒打发的股份的回复的后代鼓捣鼓捣 阿打算打打打算的啊大大十多万鹅鹅鹅我热潮中在大大</span></p><p><br></p><p><br></p><p><span style=\"color: rgb(255, 251, 143);\">撒旦教奥斯卡了的骄傲了看得见卡的好拉屎的金卡价saduoiejwrwmflkvnfslvlkaosfjdfms</span></p><p><span style=\"color: rgb(255, 251, 143);\">舒服vhskdjklvnmxmvxsdfsdfsdfjjsj;lrjewklrjkl</span></p><p><img src=\"https://img1.baidu.com/it/u=186439391,629096241&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=740\" alt=\"\" data-href=\"\" style=\"\"/></p><p><br></p><p><br></p><p><span style=\"color: rgb(255, 77, 79);\">结尾。。。。。。asd.asdas撒as大大啊我u哦IE问问瑞荣 &nbsp;1231312313112313132131231</span><div class=\"media-wrap video-wrap\"><video controls=\"\" class=\"media-wrap video-wrap\" src=\"https://images.artvrpro.com/activity%2F1668587203846-7_1.MP4.7_1.MP4\"></video></div></p>"

 webview = findViewById(R.id.webview)
 webview.setBackgroundColor(Color.parseColor("#020D1B"))
 //调用
 initWebViewContent(bean.content)
  /**
     * 传入参数为String的时候
     */
    @SuppressLint("NewApi")
    private fun initWebViewContent(content: String) {
        var content = content
        val wvcc: WebChromeClient = WebChromeClient()
        val webSettings: WebSettings = webview.getSettings()
        webSettings.setJavaScriptEnabled(true)
        webSettings.setUseWideViewPort(true) // 关键点
        webSettings.setAllowFileAccess(true) // 允许访问文件
        webSettings.setSupportZoom(true) // 支持缩放w
        webSettings.setLoadWithOverviewMode(true)
        webSettings.setDatabaseEnabled(true)
        webSettings.setDomStorageEnabled(true)
        webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN)
        webSettings.setTextZoom(200)
        webview.setWebChromeClient(wvcc)
        content = content.replace("background-color:#020D1B".toRegex(), "background-color:#020D1B")
        CSS_Content = content
        webview.loadDataWithBaseURL(
            null,
            CSS_STYLE + getNewContent(content),
            "text/html",
            "utf-8",
            null
        )

        webview.setWebViewClient(object : WebViewClient() {
            override fun onPageFinished(view: WebView, url: String?) {
                super.onPageFinished(view, url)


                // 解决NestedScrollView嵌套WebView底部留白问题
//                        webview.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;

                //这是我找到的最简单的方法(例如,将文本颜色更改为白色):
                view.loadUrl(
                    "javascript:document.body.style.setProperty(\"color\", \"white\");"
                )
            }
        })
    }

    //视频屏幕宽度 解决视频超出屏幕宽度的问题
    private fun getNewContent(htmlText: String): String {
        val doc: Document = Jsoup.parse(htmlText)
        val elements: Elements = doc.getElementsByTag("img")
        for (element in elements) {
            element.attr("width", "100%").attr("height", "auto")
        }
        val elementsVideo: Elements = doc.getElementsByTag("video")
        for (element in elementsVideo) {
            element.attr("width", "100%").attr("height", "auto")
        }
        return doc.toString()
    }


    override fun onDestroy() {
        super.onDestroy()
        if (webview != null) {    //销毁界面时要释放webview,否则退出页面还会播放视频音频
            webview.loadUrl("about:blank")
//            webview = null
        }
    }

总结

以上便是通过加载js的方式预览PDF文件了,都有注释自己慢慢去理解,有问题欢迎提出并指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叶已初秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值