最近项目里,需要集成PDF阅读,翻阅了很多网站,发现Android系统不支持PDF阅读,网上现有的库和插件,都会增大apk的体积,综合比较了一下,解决方案有如下几种:
1.谷歌提供了在线阅读,在webView中调用GoogleDocs
但是由于国内手机无法获取Google提供支持,所以这种方案基本被否决,其使用方式如下:
public void setDocumentPath(final String path) {
WebView webView = (WebView) findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setPluginsEnabled(true);
webView.loadUrl("https://docs.google.com/viewer?url=http://www./cms/wwwroot/ng/downLoad/011615200732.pdf");
}
2.最简单的方式就是跳第三方的浏览器,下载阅读
下载完成后,如果浏览器含有PDF阅读插件,便可以在浏览器中直接打开,否则还要下载PDF阅读器方可阅读,使用方式如下:
public Intent getPdfFileIntent(File file) {
Intent intent = new Intent("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri uri = Uri.fromFile(file);
intent.setDataAndType(uri, "application/pdf");
return Intent.createChooser(intent, "Open File");
}
3.利用腾讯X5内核浏览器的Tbs,下载阅读
下载完成后,首先加载X5插件,中间会有一段时间特别缓慢,官方建议可以再application中进行加载初始化,但这会导致APP启动时间增长,更糟糕的是卡在启动页,出现的原因:手机没有X5内核浏览器,需要开启服务下载(鉴于自己的项目,仅仅是猜测),其具体的使用方法如下(我这里自己封装了一个下载工具,下面的几种方式都采用这个工具):
import android.app.DownloadManager;
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;
import com.mysteel.android.steelphone.bean.ebe.EBPdfMsg;
import org.greenrobot.eventbus.EventBus;
import java.io.File;
import static android.app.DownloadManager.ACTION_DOWNLOAD_COMPLETE;
import static android.app.DownloadManager.ACTION_NOTIFICATION_CLICKED;
import static android.app.DownloadManager.STATUS_FAILED;
import static android.app.DownloadManager.STATUS_PAUSED;
import static android.app.DownloadManager.STATUS_PENDING;
import static android.app.DownloadManager.STATUS_RUNNING;
import static android.app.DownloadManager.STATUS_SUCCESSFUL;
public class DownloadPdfUtils {
//下载器
private DownloadManager downloadManager;
//上下文
private Context mContext;
//下载的ID
private long downloadId;
private boolean mReceiverTag = false;
public DownloadPdfUtils(Context context) {
if (context == null) {
return;
}
this.mContext = context;
}
public void downloadUrl(String url, String name) {
//创建下载任务
Request request = new Request(Uri.parse(url));
//移动网络下是否允许
request.setAllowedOverRoaming(false);
//在通知栏中显示,默认就是显示的
request.setNotificationVisibility(Request.VISIBILITY_HIDDEN);
request.setVisibleInDownloadsUi(true);
//设置下载路径
final String sdcard = Environment.getExternalStorageDirectory().getAbsolutePath();
final String pdfPath = sdcard + "/mysteel/pdf";
if (!(new File(pdfPath)).exists()) {
(new File(pdfPath)).mkdirs();
}
File file = new File(pdfPath, name);
request.setDestinationUri(Uri.fromFile(file));
downloadManager = ((DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE));
removePdfFile();
downloadId = downloadManager.enqueue(request);
mContext.registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
mReceiverTag = true;
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == ACTION_DOWNLOAD_COMPLETE) {
checkStatus();
}
}
};
private BroadcastReceiver receiver1 = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == DownloadManager.ACTION_NOTIFICATION_CLICKED) {
downloadManager.remove(downloadId);
}
}
};
/**
* 下载状态
*/
private void checkStatus() {
Query query = new Query();
//通过下载的ID查找
Cursor cursor = downloadManager.query(query);
if (cursor.moveToFirst()) {
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
switch (status) {
case STATUS_PAUSED:
break;
case STATUS_PENDING:
break;
case STATUS_RUNNING:
break;
case STATUS_SUCCESSFUL:
EBPdfMsg event = new EBPdfMsg();
event.setUrl(queryUrl());
EventBus.getDefault().post(event);
break;
case STATUS_FAILED:
break;
default:
break;
}
}
cursor.close();
mContext.unregisterReceiver(receiver);
mReceiverTag = false;
}
/**
* 返回文件下载路径
*
* @return
*/
public String queryUrl() {
String url = "";
if (downloadId != -1) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
Cursor cursor = downloadManager.query(query);
if (cursor != null) {
if (cursor.moveToFirst()) {
url = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
}
}
assert cursor != null;
cursor.close();
}
return url;
}
/**
* 取消下载
*/
public void cancelDownload() {
mContext.registerReceiver(receiver1, new IntentFilter(ACTION_NOTIFICATION_CLICKED));
}
/**
* 取消广播注册
*/
public void unRegister() {
if (mReceiverTag) {
mContext.unregisterReceiver(receiver);
}
mContext.unregisterReceiver(receiver1);
}
/**
* 删除PDF文件
*/
private void removePdfFile() {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
Cursor cursor = downloadManager.query(query);
if (cursor != null) {
while (cursor.moveToNext()) {
if (cursor.getColumnIndex(DownloadManager.COLUMN_ID) != -1) {
downloadManager.remove(cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID)));
}
}
}
assert cursor != null;
cursor.close();
}
}
具体的集成方式可以去腾讯官网查看:https://x5.tencent.com/
首先,在Application中初始化X5浏览,虽说这是一个轻量级的不会造成ANR但是还是采用服务比较稳妥,具体如下:
写一个服务类:
import android.app.IntentService;
import android.content.Intent;
import android.support.annotation.Nullable;
import com.tencent.smtt.sdk.QbSdk;
/**
* 预加载X5内核浏览器
*/
public class X5CorePreLoadService extends IntentService{
private static final String TAG = X5CorePreLoadService.class.getSimpleName();
public X5CorePreLoadService() {
super(TAG);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//在这里添加我们要执行的代码,Intent中可以保存我们所需的数据,
//每一次通过Intent发送的命令将被顺序执行
initX5();
}
/**
* 初始化X5内核
*/
private void initX5() {
if (!QbSdk.isTbsCoreInited()) {
QbSdk.preInit(getApplicationContext(), null);// 设置X5初始化完成的回调接口
}
QbSdk.initX5Environment(getApplicationContext(), cb);
}
QbSdk.PreInitCallback cb = new QbSdk.PreInitCallback() {
@Override
public void onViewInitFinished(boolean arg0) {
// TODO Auto-generated method stub
//初始化完成回调
}
@Override
public void onCoreInitFinished() {
// TODO Auto-generated method stub
}
};
}
其次,在Application中初始化:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
preInitX5Core();
}
/**
* 初始化X5内核
*/
private void preInitX5Core() {
//预加载x5内核
Intent intent = new Intent(this, X5CorePreLoadService.class);
startService(intent);
}
}
在Activity中显示,这里需要注意6.0权限,我用的EasyPermission,具体用法:https://github.com/googlesamples/easypermissions,TBS在Activity中使用
public class WebActivity extends AppCompatActivity implements TbsReaderView.ReaderCallback {
private static final int PERMISSION_CODE3 = 1;
private TbsReaderView mTbsReaderView;
private FrameLayout mPdf;
public static final String url = "";//PDF链接
private DownloadPdfUtils downloadUtils;
private String[] PERMISSION_ARRAYS3 = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web);
mPdf = findViewById(R.id.read_pdf);
mTbsReaderView = new TbsReaderView(this, this);
mPdf.addView(mTbsReaderView, new RelativeLayout.LayoutParams(-1, -1));
downloadUtils = new DownloadPdfUtils(this);
if (url != null) {
checkPermission();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(EBPdfMsg event) {
String urls = event.getUrl();
displayFile(new File(urls).getName());
}
/**
* 检查是否有权限
*/
@AfterPermissionGranted(PERMISSION_CODE3)
private void checkPermission() {
if (EasyPermissions.hasPermissions(this, PERMISSION_ARRAYS3)) {
downloadUtils.downloadUrl(url, pdfName);
} else {
//Auto to do
}
}
/**
* PDF阅读
*
* @param name
*/
private void displayFile(String name) {
Bundle bundle = new Bundle();
bundle.putString("filePath", Environment.getExternalStorageDirectory().getAbsolutePath() + "/mysteel/pdf" + File.separator + name);
bundle.putString("tempPath", Environment.getExternalStorageDirectory().getAbsolutePath() + "/mysteel/pdf");
boolean result = mTbsReaderView.preOpen(getFileType(name), false);
if (result) {
mTbsReaderView.openFile(bundle);
}
}
/**
* 文件类型是否是.pdf
*
* @param paramString
* @return
*/
public static String getFileType(String paramString) {
String str = "";
if (TextUtils.isEmpty(paramString)) {
return str;
}
int i = paramString.lastIndexOf('.');
if (i <= -1) {
return str;
}
try {
str = paramString.substring(i + 1);
} catch (Exception e) {
e.printStackTrace();
}
return str;
}
@Override
public void onCallBackAction(Integer integer, Object o, Object o1) {
}
}
activity的xml如下:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas./apk/res/android"
android:id="@+id/read_pdf"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
使用方式如下:
public class ReadPdfActivity extends AppCompatActivity {
private static final int PERMISSION_CODE3 = 1;
private PDFView mPdf ;
public static final String url = "";//PDF链接
private DownloadPdfUtils downloadUtils;
private String[] PERMISSION_ARRAYS3 = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web);
mPdf = findViewById(R.id.read_pdf);
downloadUtils = new DownloadPdfUtils(this);
if (url != null) {
checkPermission();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(EBPdfMsg event) {
String urls = event.getUrl();
displayFile(new File(urls).getName());
}
/**
* 检查是否有权限
*/
@AfterPermissionGranted(PERMISSION_CODE3)
private void checkPermission() {
if (EasyPermissions.hasPermissions(this, PERMISSION_ARRAYS3)) {
downloadUtils.downloadUrl(url, pdfName);
} else {
//Auto to do
}
}
/**
* PDF阅读
*
* @param name
*/
private void displayFile(String name) {
mReadPdf.fromFile(new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/mysteel/pdf" + File.separator + name))
.swipeHorizontal(false)
.enableDoubletap(true)
.defaultPage(0)
.onError(new OnErrorListener() {
@Override
public void onError(Throwable t) {
showEmpty();
}
})
.load();
}
}
总结:
这五种方式,体积最小的是腾讯的TBS,体积最大的PDFView,个人比较推崇使用PDFView,pdfjs这个实现方式有三种,服务器前后端配合这种我没有使用,其他两者种,在下拉滑动时,可能会出现空白页,界面不太友好,但最终能显示。当然刨去网络问题还是Google docs最好,但是这种在国内无法使用。以上代码中有误的地方还望各位小伙伴指出。
|