安卓实现应用内展示PDF

github地址:https://github.com/barteksc/AndroidPdfViewer
首先添加依赖:
//PDFView
implementation 'com.github.barteksc:android-pdf-viewer:3.0.0-beta.5'

应用内展示PDF的方案有两种:
1.将PDF转换成流,PDFView是支持直接加载流的;
2.是先将PDF下载到本地,然后加载本地文件。

下面看代码实现:
第一种方式:需要添加网络权限
布局文件如下:很简单,就放一个标题栏和PDFView




    

        

        

    

    

Activity中的代码:

package com.keke.pdfview;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;

import com.github.barteksc.pdfviewer.PDFView;
import com.hjq.toast.ToastUtils;

import java.io.IOException;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * @author Luffy
 */
public class ShowPdfByIoActivity extends Activity {
    private static final String TAG = "MainActivity";
    private PDFView mPdfView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_show_pdf_by_io);
        initViewsAndBaseDate();
    }

    private void initViewsAndBaseDate() {
        Intent intent = getIntent();
        assert intent!=null;
        String mPdfUrl = intent.getStringExtra("url");
        mPdfView = findViewById(R.id.pdfView);
        findViewById(R.id.ivBack).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });

        asynDownFile(mPdfUrl);
    }

    /**
     * 异步下载文件
     *
     * @param url 文件的url
     */
    private void asynDownFile(String url) {
        OkHttpClient mOkHttpClient = new OkHttpClient();
        Request request = new Request.Builder().url(url).build();
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                ToastUtils.show("加载合同失败,请重试!");
                Log.d(TAG, "onFailure: e == " + e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) {
                Log.d(TAG, "onResponse: response == " + response);
                if (response.body() != null) {
                    ShowPdfUtil.getInstance().showPdfByInputStream(response.body().byteStream(), mPdfView);
                }
            }
        });
    }
}

ShowPdfUtil代码如下:

package com.keke.pdfview;

import com.github.barteksc.pdfviewer.PDFView;
import com.github.barteksc.pdfviewer.listener.OnErrorListener;
import com.github.barteksc.pdfviewer.listener.OnLoadCompleteListener;
import com.hjq.toast.ToastUtils;

import java.io.File;
import java.io.InputStream;

/**
 * @author : Luffy Harris
 * e-mail : [email protected]
 * phone  : 13002903389
 * date   : 2019/12/12-17:46
 * desc   : 应用内打开PDF文档的工具类,依赖 AndroidPdfViewer这个库,github链接:https://github.com/barteksc/AndroidPdfViewer
 * version: 1.0
 */
public class ShowPdfUtil implements OnLoadCompleteListener, OnErrorListener {

    private ShowPdfUtil() {
    }

    private static class ShowPdfUtilBuilder {
        private static final ShowPdfUtil SHOW_PDF_UTIL = new ShowPdfUtil();

    }

    public static ShowPdfUtil getInstance() {
        return ShowPdfUtilBuilder.SHOW_PDF_UTIL;
    }


    public void showPdfByInputStream(InputStream inputStream, PDFView pdfView) {
        setConfigurator(pdfView.fromStream(inputStream));
    }

    public void showAssetPdf(String assetPath, PDFView pdfView) {
        setConfigurator(pdfView.fromAsset(assetPath));
    }

    /**
     * 查看下载的PDF
     */
    public void showPdfByDownloadFile(File pdfFile, PDFView pdfView) {
        setConfigurator(pdfView.fromFile(pdfFile));
    }

    private void setConfigurator(PDFView.Configurator configurator) {
        configurator.defaultPage(0)
                .onLoad(this)
                .onError(this)
                .enableAnnotationRendering(true)
                .load();
    }

    @Override
    public void loadComplete(int nbPages) {
        ToastUtils.show("加载文件成功");
    }

    @Override
    public void onError(Throwable t) {
        ToastUtils.show("加载文件失败,请重试");
    }
}

就是这么简单,欧了~但运行之后报错了,报错如下:

CLEARTEXT communication to happyleasing.cn not permitted by network security policy 大概意思是说网络安全策略不允许与铭文链接进行通信,各种百度谷歌之后,发现在Android高版本(27以上不包括27)上,Google为了保证用户数据和设备的安全性,针对 P版本的应用程序作了一个网络安全策略,这个策略就是网络传输默认使用加密链接,未来都不能明码传输,需要使用下一代(Transport Layer Security)传输层安全协议,而 Android Nougat 和 Oreo 则不受影响。

既然知道原因了,那怎么办解决呢?我这里也提供三个方案,大家有更好的方案可以留言区留言与我交流~

  • 将http请求改为https
  • targetSdkVersion 降到27或27以下版本
  • 更改网络安全配置

前两个都好理解,大家一看就知道怎么整,所以这里主要说说第三种方案:
首先在 res文件夹下新建 xml文件夹,然后创建一个 network_security_config.xml文件,文件内容如下:



    

在AndroidMenifest.xml的 application标签上添加如下配置:

android:networkSecurityConfig="@xml/network_security_config"

运行项目,欧了~
其实还有一个方案,直接在application标签上设置属性:android:usesCleartextTraffic="true" ,完美~

第二种方式:除了需要添加网络权限之外,还需要读写权限,下面是Activity中的代码:

package com.keke.pdfview;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.Window;

import com.github.barteksc.pdfviewer.PDFView;
import com.hjq.toast.ToastUtils;

import java.io.File;
import java.io.IOException;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okio.BufferedSink;
import okio.Okio;
import okio.Sink;

/**
 * @author Luffy
 */
public class ShowPdfByDownloadActivity extends Activity {
    @BindView(R.id.pdfView)
    PDFView pdfView;
    private Unbinder bind;
    /**
     * 申请权限的请求码
     */
    private static final int REQUEST_CODE_EXTERNAL_STORAGE = 0x0001;
    /**
     * 需要读写权限
     */
    private static String[] PERMISSIONS_STORAGE = {
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE};
    /**
     * pdf文档的Url
     */
    private String mPdfUrl;
    /**
     * 最终需要加载的是File类型的pdf
     */
    private File pdfFile;
    /**
     * 下载文件完成的标识
     */
    private final int DOWNLOAD_SUCCESSFUL = 200;
    @SuppressLint("HandlerLeak")
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == DOWNLOAD_SUCCESSFUL) {
                ShowPdfUtil.getInstance().showPdfByDownloadFile(pdfFile, pdfView);
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_show_pdf);
        bind = ButterKnife.bind(this);
        initViewsAndBaseDate();
        checkPerMission();
    }

    /**
     * 初始化View和基本数据
     */
    private void initViewsAndBaseDate() {
        Intent intent = getIntent();
        assert intent != null;
        mPdfUrl = intent.getStringExtra("url");
    }

    /**
     * 检测权限
     */
    private void checkPerMission() {
        //M版本之后才需要申请权限
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            int permission = ActivityCompat.checkSelfPermission(this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE);
            //无权限时申请权限
            if (permission != PackageManager.PERMISSION_GRANTED) {
                // We don't have permission so prompt the user
                ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE,
                        REQUEST_CODE_EXTERNAL_STORAGE);
            } else {
                downloadPdf();
            }
        } else {
            downloadPdf();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (REQUEST_CODE_EXTERNAL_STORAGE == requestCode && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            downloadPdf();
        } else {
            //判断是否勾选了“禁止后不再提示”选框
            boolean showRequestPermission = ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[0]);
            if (!showRequestPermission) {
                ToastUtils.show("您已禁止提醒授权,如需正常使用,请到应用权限管理界面进行授权");
            }
        }
    }

    /**
     * 开始下载PDF
     */
    private void downloadPdf() {
        //应用缓存路径
        String cacheUrl = getCacheDir().getAbsolutePath();
        //文件名称
        String pdfName = mPdfUrl.substring(mPdfUrl.lastIndexOf("/") + 1);
        pdfFile = new File(cacheUrl, pdfName);
        if (pdfFile.exists()) {
            ShowPdfUtil.getInstance().showPdfByDownloadFile(pdfFile, pdfView);
        } else {
            Request request = new Request.Builder()
                    .url(mPdfUrl)
                    .build();

            new OkHttpClient().newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    // 下载失败
                    ToastUtils.show("下载失败,请重试");
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    Sink sink;
                    BufferedSink bufferedSink = null;
                    try {
                        if (!pdfFile.exists()) {
                            Log.e("Luffy", "onResponse: no file");
                            boolean newFile = pdfFile.createNewFile();
                            if (newFile) {
                                sink = Okio.sink(pdfFile);
                                bufferedSink = Okio.buffer(sink);
                                if (response.body() != null) {
                                    bufferedSink.writeAll(response.body().source());
                                }
                                bufferedSink.close();
                                Message message = Message.obtain();
                                message.what = DOWNLOAD_SUCCESSFUL;
                                handler.sendMessage(message);
                            }
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        if (bufferedSink != null) {
                            bufferedSink.close();
                        }
                    }
                }
            });
        }
    }

    @OnClick(R.id.ivBack)
    public void onViewClicked() {
        finish();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        bind.unbind();
    }
}

完整项目已提交至github上:https://github.com/ZNKForSky/PdfView,欢迎提问~

你可能感兴趣的:(Android)