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 则不受影响。
既然知道原因了,那怎么办解决呢?我这里也提供三个方案,大家有更好的方案可以留言区留言与我交流~
前两个都好理解,大家一看就知道怎么整,所以这里主要说说第三种方案:
首先在 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,欢迎提问~