效果图:

android pdf 操作 android显示pdf_很好用

上图附件下的fragment内显示的就是pdf

前言:

兼容android 8.0,直接获取服务端返的url(http.....pdf)直接打开。

第一步:

android pdf 操作 android显示pdf_android pdf 操作_02

在assets文件下,粘贴过去pdfjs的整个包。(在文章尾部提供)

第二步:

<!-- 指纹及手势密码的权限 -->
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="com.fingerprints.service.ACCESS_FINGERPRINT_MANAGER" />
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
<!-- 计歩额外用的权限 -->
<uses-feature android:name="android.hardware.sensor.accelerometer" />

<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Android8.0以后需要加这个权限,不然闪退 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- 计歩使用 -->
<uses-feature
    android:name="android.hardware.sensor.stepcounter"
    android:required="true" />
<uses-feature
    android:name="android.hardware.sensor.stepdetector"
    android:required="true" />

<!-- 允许应用程序改变网络状态 -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<!-- 允许应用程序改变WIFI连接状态 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!-- 允许应用程序访问有关的网络信息 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 允许应用程序访问WIFI网卡的网络信息 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 连接网络权限,用于执行云端语音能力 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 读取手机信息权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 读取联系人权限,上传联系人需要用到此权限 -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<!-- 外存储写权限,构建语法需要用到此权限 -->
<uses-permission android:name="android.permission.VIBRATE" />
<!-- 外存储读权限,构建语法需要用到此权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 定位信息是敏感信息,可通过Setting.setLocationEnable(false)关闭定位请求 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 拍照权限开始 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 读写权限开始 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission
    android:name="android.permission.CHANGE_CONFIGURATION"
    tools:ignore="ProtectedPermissions" />
<!-- 蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission
    android:name="android.permission.BLUETOOTH_PRIVILEGED"
    tools:ignore="ProtectedPermissions" />
<!-- 允许应用程序完全使用网络 -->
<uses-permission
    android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
    tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
<!-- 配置权限,用来记录应用配置信息 -->
<uses-permission
    android:name="android.permission.WRITE_SETTINGS"
    tools:ignore="ProtectedPermissions" />
<!-- 微信/支付宝支付权限 -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--版本更新安卓8.0需要的权限-->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- 从Android 9.0(API级别28)开始,默认情况下禁用明文支持。因此http的url均无法在webView中加载,设置usesCleartextTraffic -->

权限为了简便,我就直接粘贴了常用的。大家自行合理删减

android pdf 操作 android显示pdf_android pdf 操作_03

在application的地方设置属性,红线标识的位置。

同时,安卓6.0以后的 动态配置权限也要执行。

第三步:

android pdf 操作 android显示pdf_android pdf 操作_04

 

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <external-path
            name="download"
            path=""/>
    </paths>
    <external-path
        name="files_root"
        path="Android/data/com.yc.stscf/" />
    <external-path
        name="external_storage_root"
        path="." />
    <external-path
        name="my_images"
        path="Android/data/com.yc.stscf/files/Pictures" />
    <root-path
        name="root_path"
        path="." />
</resources>

大家根据自己的包名去更改。 

android pdf 操作 android显示pdf_android pdf 操作_05

第四步: (开始写内容)

布局:

<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/white">    <com.yc.stscf.widget.ExtendedWebView
         android:id="@+id/view_web"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 </LinearLayout>

很简单的放个自定义的webView,采用自定义的是因为这个界面在viewpager中滑动冲突

自定义控件:

package com.yc.stscf.widget;

import android.annotation.SuppressLint;
import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.webkit.WebView;

/**
 * ================================================
 *
 * @author :Vip
 * @version :V 1.0.0
 * @date :2019/7/24 16:51
 * 描    述:主要解决viewPager嵌套webView横向滚动问题
 * 修订历史:
 * ================================================
 */

public class ExtendedWebView extends WebView {

    private boolean isScrollX = false;

    public ExtendedWebView(Context context) {
        super(context);
    }

    public ExtendedWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (MotionEventCompat.getPointerCount(event) == 1) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    isScrollX = false;
                    //事件由webView处理
                    getParent().getParent()
                            .requestDisallowInterceptTouchEvent(true);
                    break;
                case MotionEvent.ACTION_MOVE:
                    //嵌套Viewpager时
                    getParent().getParent()
                            .requestDisallowInterceptTouchEvent(!isScrollX);
                    break;
                default:
                    getParent().getParent()
                            .requestDisallowInterceptTouchEvent(false);
            }
        } else {
            //使webView可以双指缩放(前提是webView必须开启缩放功能,并且加载的网页也支持缩放)
            getParent().getParent().
                    requestDisallowInterceptTouchEvent(true);
        }
        return super.onTouchEvent(event);
    }

    /**
     * 当webView滚动到边界时执行
     **/
    @Override
    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
        isScrollX = clampedX;
    }
}

代码:

package com.yc.stscf.fragment.contract;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.yc.stscf.R;
import com.yc.stscf.base.BaseFragment;
import com.yc.stscf.widget.ExtendedWebView;

import butterknife.BindView;

/**
 * ================================================
 *
 * @author :Vip
 * @version :V 1.0.0
 * @date :2019/7/9 15:47
 * 描    述:pdf展示
 * 修订历史:
 * ================================================
 */

public class ContractEnclosureFragment extends BaseFragment {
    @BindView(R.id.view_web)
    ExtendedWebView pdfShowWebView;
    /**
     * 总布局
     **/
    private View view = null;
    /**
     * 标志位,标志已经初始化完成
     **/
    private boolean isPrepared;

    @SuppressLint("InflateParams")
    @Override
    protected View initLayout(LayoutInflater inflater, ViewGroup container, boolean b) {
        view = inflater.inflate(R.layout.fragment_contract_enclosure, null);
        isPrepared = true;
        return view;
    }

    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void initView(Bundle savedInstanceState) {

        pdfShowWebView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                // 返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器
                view.loadUrl(url);
                return true;
            }
        });
        WebSettings settings = pdfShowWebView.getSettings();
        settings.setSavePassword(false);
        settings.setJavaScriptEnabled(true);
        settings.setAllowFileAccessFromFileURLs(true);
        settings.setAllowUniversalAccessFromFileURLs(true);
        settings.setBuiltInZoomControls(true);
        pdfShowWebView.requestDisallowInterceptTouchEvent(true);
        String mPdfFilePath = "http://114.115.152.132:80/lessonDoc/1540185667975.pdf";
        pdfShowWebView.setWebChromeClient(new WebChromeClient());
//        if (!"".equals(mPdfFilePath)) {
//            byte[] bytes = null;
//            try {// 获取以字符编码为utf-8的字符
//                bytes = mPdfFilePath.getBytes("UTF-8");
//            } catch (UnsupportedEncodingException e) {
//                e.printStackTrace();
//            }
//            if (bytes != null) {
//                // BASE64转码
//                mPdfFilePath = new Base64Encoder().encode(bytes);
//            }
//        }
        //TODO 采用无样式的,则引用index.html?不需要file=,并且mPdfFilePath不需要转码。
        pdfShowWebView.loadUrl("file:///android_asset/pdfjs/web/index.html?" + mPdfFilePath);
        //TODO 采用有样式的,则引用 viewer.html?file=,需要mPdfFilePath转码
//        pdfShowWebView.loadUrl("file:///android_asset/pdfjs/web/viewer.html?file=" + mPdfFilePath);
    }

    @Override
    protected void lazyLoad() {
        if (!isPrepared || !isVisible) {
            return;
        }
    }


}

baseFragment是我自定义的,可以采用原始的fragment去继承。 

采用了2种方式。在pdf的资源内已经继承完毕。分为1:全屏的仅仅显示pdf,2:pdf顶部带导航栏,放大缩小快速跳转定位,打印等。 建议方式一,方式一的样式更加美观。方式二是原生的,样式会丑些,当然也是可以修改的,需要掌握一定前端功底,改css样式。 在代码中已经描述了。方式二级(采用有样式的),将注释掉的放开、Base64Encoder是做转码用的,不适用的话加载不出来

android pdf 操作 android显示pdf_在线打开pdf_06

配合在线显示pdf的类

package com.yc.stscf.utils;

import java.io.IOException;
import java.io.OutputStream;

/**
 * ================================================
 *
 * @author:Vip 版    本:V4.1.4
 * 创建日期:2018/6/29
 * 描    述:配合在线预览pdf
 * 修订历史:
 * ================================================
 */
public class Base64Encoder extends CharacterEncoder {
    @Override
    protected int bytesPerAtom() {
        return 3;
    }

    @Override
    protected int bytesPerLine() {
        return 57;
    }

    private static final char[] PEM_ARRAY =
            {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
                    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a',
                    'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
                    'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
                    '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '='};

    @Override
    protected void encodeAtom(OutputStream paramOutputStream,
                              byte[] paramArrayOfByte, int paramInt1, int paramInt2)
            throws IOException {
        int i;
        int j;
        int k;
        if (paramInt2 == 1) {
            i = paramArrayOfByte[paramInt1];
            j = 0;
            k = 0;
            paramOutputStream.write(PEM_ARRAY[(i >>> 2 & 0x3F)]);
            paramOutputStream
                    .write(PEM_ARRAY[((i << 4 & 0x30) + (j >>> 4 & 0xF))]);
            paramOutputStream.write(61);
            paramOutputStream.write(61);
        } else if (paramInt2 == 2) {
            i = paramArrayOfByte[paramInt1];
            j = paramArrayOfByte[(paramInt1 + 1)];
            k = 0;
            paramOutputStream.write(PEM_ARRAY[(i >>> 2 & 0x3F)]);
            paramOutputStream
                    .write(PEM_ARRAY[((i << 4 & 0x30) + (j >>> 4 & 0xF))]);
            paramOutputStream
                    .write(PEM_ARRAY[((j << 2 & 0x3C) + (k >>> 6 & 0x3))]);
            paramOutputStream.write(61);
        } else {
            i = paramArrayOfByte[paramInt1];
            j = paramArrayOfByte[(paramInt1 + 1)];
            k = paramArrayOfByte[(paramInt1 + 2)];
            paramOutputStream.write(PEM_ARRAY[(i >>> 2 & 0x3F)]);
            paramOutputStream
                    .write(PEM_ARRAY[((i << 4 & 0x30) + (j >>> 4 & 0xF))]);
            paramOutputStream
                    .write(PEM_ARRAY[((j << 2 & 0x3C) + (k >>> 6 & 0x3))]);
            paramOutputStream.write(PEM_ARRAY[(k & 0x3F)]);
        }
    }
}

 父类

package com.yc.stscf.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.ByteBuffer;

/**
 * ================================================
 *
 * @author:Vip 版    本:V4.1.4
 * 创建日期:2018/6/29
 * 描    述:配合在线预览pdf
 * 修订历史:
 * ================================================
 */
public abstract class CharacterEncoder {
    private PrintStream pStream;

    protected abstract int bytesPerAtom();

    protected abstract int bytesPerLine();

    private void encodeBufferPrefix(OutputStream paramOutputStream)
            throws IOException {
        this.pStream = new PrintStream(paramOutputStream);
    }

    private void encodeBufferSuffix(OutputStream paramOutputStream)
            throws IOException {
    }

    private void encodeLinePrefix(OutputStream paramOutputStream, int paramInt)
            throws IOException {
    }

    private void encodeLineSuffix(OutputStream paramOutputStream)
            throws IOException {
        this.pStream.println();
    }

    protected abstract void encodeAtom(OutputStream paramOutputStream, byte[] paramArrayOfByte, int paramInt1, int paramInt2)
            throws IOException;

    private int readFully(InputStream paramInputStream, byte[] paramArrayOfByte)
            throws IOException {
        for (int i = 0; i < paramArrayOfByte.length; i++) {
            int j = paramInputStream.read();
            if (j == -1) {
                return i;
            }
            paramArrayOfByte[i] = ((byte) j);
        }
        return paramArrayOfByte.length;
    }

    private void encode(InputStream paramInputStream, OutputStream paramOutputStream)
            throws IOException {
        byte[] arrayOfByte = new byte[bytesPerLine()];

        encodeBufferPrefix(paramOutputStream);
        for (; ; ) {
            int j = readFully(paramInputStream, arrayOfByte);
            if (j == 0) {
                break;
            }
            encodeLinePrefix(paramOutputStream, j);
            for (int i = 0; i < j; i += bytesPerAtom()) {
                if (i + bytesPerAtom() <= j) {
                    encodeAtom(paramOutputStream, arrayOfByte, i, bytesPerAtom());
                } else {
                    encodeAtom(paramOutputStream, arrayOfByte, i, j - i);
                }
            }
            if (j < bytesPerLine()) {
                break;
            }
            encodeLineSuffix(paramOutputStream);
        }
        encodeBufferSuffix(paramOutputStream);
    }

    private void encode(byte[] paramArrayOfByte, OutputStream paramOutputStream)
            throws IOException {
        ByteArrayInputStream localByteArrayInputStream = new ByteArrayInputStream(paramArrayOfByte);
        encode(localByteArrayInputStream, paramOutputStream);
    }

    public String encode(byte[] paramArrayOfByte) {
        ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();
        ByteArrayInputStream localByteArrayInputStream = new ByteArrayInputStream(paramArrayOfByte);
        String str = null;
        try {
            encode(localByteArrayInputStream, localByteArrayOutputStream);

            str = localByteArrayOutputStream.toString("8859_1");
        } catch (Exception localException) {
            throw new Error("CharacterEncoder.encode internal error");
        }
        return str;
    }

    private byte[] getBytes(ByteBuffer paramByteBuffer) {
        byte[] localObject = null;
        if (paramByteBuffer.hasArray()) {
            byte[] arrayOfByte = paramByteBuffer.array();
            if ((arrayOfByte.length == paramByteBuffer.capacity()) && (arrayOfByte.length == paramByteBuffer.remaining())) {
                localObject = arrayOfByte;
                paramByteBuffer.position(paramByteBuffer.limit());
            }
        }
        if (localObject == null) {
            localObject = new byte[paramByteBuffer.remaining()];


            paramByteBuffer.get((byte[]) localObject);
        }
        return localObject;
    }

    public void encode(ByteBuffer paramByteBuffer, OutputStream paramOutputStream)
            throws IOException {
        byte[] arrayOfByte = getBytes(paramByteBuffer);
        encode(arrayOfByte, paramOutputStream);
    }

    public String encode(ByteBuffer paramByteBuffer) {
        byte[] arrayOfByte = getBytes(paramByteBuffer);
        return encode(arrayOfByte);
    }

    private void encodeBuffer(InputStream paramInputStream, OutputStream paramOutputStream)
            throws IOException {
        byte[] arrayOfByte = new byte[bytesPerLine()];

        encodeBufferPrefix(paramOutputStream);
        for (; ; ) {
            int j = readFully(paramInputStream, arrayOfByte);
            if (j != 0) {
                encodeLinePrefix(paramOutputStream, j);
                for (int i = 0; i < j; i += bytesPerAtom()) {
                    if (i + bytesPerAtom() <= j) {
                        encodeAtom(paramOutputStream, arrayOfByte, i, bytesPerAtom());
                    } else {
                        encodeAtom(paramOutputStream, arrayOfByte, i, j - i);
                    }
                }
                encodeLineSuffix(paramOutputStream);
                if (j < bytesPerLine()) {
                    break;
                }
            }
        }
        encodeBufferSuffix(paramOutputStream);
    }

    private void encodeBuffer(byte[] paramArrayOfByte, OutputStream paramOutputStream)
            throws IOException {
        ByteArrayInputStream localByteArrayInputStream = new ByteArrayInputStream(paramArrayOfByte);
        encodeBuffer(localByteArrayInputStream, paramOutputStream);
    }

    private String encodeBuffer(byte[] paramArrayOfByte) {
        ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();
        ByteArrayInputStream localByteArrayInputStream = new ByteArrayInputStream(paramArrayOfByte);
        try {
            encodeBuffer(localByteArrayInputStream, localByteArrayOutputStream);
        } catch (Exception localException) {
            throw new Error("CharacterEncoder.encodeBuffer internal error");
        }
        return localByteArrayOutputStream.toString();
    }

    public void encodeBuffer(ByteBuffer paramByteBuffer, OutputStream paramOutputStream)
            throws IOException {
        byte[] arrayOfByte = getBytes(paramByteBuffer);
        encodeBuffer(arrayOfByte, paramOutputStream);
    }

    public String encodeBuffer(ByteBuffer paramByteBuffer) {
        byte[] arrayOfByte = getBytes(paramByteBuffer);
        return encodeBuffer(arrayOfByte);
    }
}

 

最后: pdf包的引用文件放在了我的下载内容里。大家去里面下载吧。我想免费提供。