Android 开发之多种方案PDF阅读
最近开发中涉及到阅读港股公告,但是HK股票的公告都是坑,居然是pdf的,所以没办法,就要研究安卓pdf阅读,期间踩了点坑……
安卓的webview与ios 的UIWebView不一样,不能够支持在线阅读pdf,其实PC端的浏览器大部分也不支持pdf预览,虽然谷歌官方给了个“http //docs google com/gviewembedded=true&url=”这样的解决方案(将pdf、doc当成图片来看),但是在天朝有一堵墙,所以这个方案直接被抛弃。PC端的Chrome与火狐都是使用js处理才实现了pdf预览,所以在安卓端现有阅读并实现兼容性的解决方案有两种:一、使用webview,但本地添加js库处理(火狐提供了开源的pdf.js),二、修改安卓底层库,使用自定义view进行pdf预览。
一、使用JS 处理支持webview阅读pdf(同样适用于web前端)
1.服务器解决
此处将使用到pdf.js(火狐的解决方案)
服务端
//安装pdf.js
$ npm install -g gulp-cli
//构建
$ gulp generic
构建完成后,把generic拷贝服务器相对目录下,就完成了。
启动服务,使用如下地址预览
http://localhost:8080/generic/web/viewer.html
最后,如果你要打开指定的pdf,这样就可以了
http://localhost:8080/generic/web/viewer.html?file=aaa.pdf
是不是很简单啊。
2.客户端解决
将https://github.com/mozilla/pdf.js/下下来放到项目的assets下面,然后将这些copy到data下或者sd卡中,pdf也下载到相对目录下,然后就可以同上一样作为本地服务器一样阅读pdf了,效果如下图
二、修改安卓底层库,使用自定义view进行pdf预览
1.官方simple PdfRendererBasic
- pdf开发三部曲
打开初始化Pdf读取器PdfRenderer
如果你是在Fragment里面使用,最好在onAttach方法初始化
/**
* Sets up a {@link android.graphics.pdf.PdfRenderer} and related resources.
*/
private void openRenderer(Context context) throws IOException {
// In this sample, we read a PDF from the assets directory.
mFileDescriptor = context.getAssets().openFd("sample.pdf").getParcelFileDescriptor();
// This is the PdfRenderer we use to render the PDF.
mPdfRenderer = new PdfRenderer(mFileDescriptor);
}
读取内容
传入指定页码,读取内容转换成bitmap图片设置到ImageView
private void showPage(int index) {
if (mPdfRenderer.getPageCount() <= index) {
return;
}
// Make sure to close the current page before opening another one.
if (null != mCurrentPage) {
mCurrentPage.close();
}
// Use `openPage` to open a specific page in PDF.
mCurrentPage = mPdfRenderer.openPage(index);
// Important: the destination bitmap must be ARGB (not RGB).
Bitmap bitmap = Bitmap.createBitmap(mCurrentPage.getWidth(), mCurrentPage.getHeight(),
Bitmap.Config.ARGB_8888);
// Here, we render the page onto the Bitmap.
// To render a portion of the page, use the second and third parameter. Pass nulls to get
// the default result.
// Pass either RENDER_MODE_FOR_DISPLAY or RENDER_MODE_FOR_PRINT for the last parameter.
mCurrentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
// We are ready to show the Bitmap to user.
mImageView.setImageBitmap(bitmap);
updateUi();
}
关闭Pdf读取器PdfRenderer
PdfRenderer.Page提供了几个方法方便与我们使用,例如getIndex(获取当前页码)、getPageCount(总页码)等。在我们离开pdf展示页面时需要对资源进行释放,关闭Pdf读取器(fragment在生命周期onDetach调用释放关闭),如果你想做的像小说阅读一样,可以记录当前的index方便下次进入直接跳转到指定位置,书签就这个原理
private void closeRenderer() throws IOException {
if (null != mCurrentPage) {
mCurrentPage.close();
}
mPdfRenderer.close();
mFileDescriptor.close();
}
如果你需要这个simple可以选择官网simple下面下载,如果你不能访问可以选择github上面下载:https://github.com/googlesamples/android-PdfRendererBasic,当然这个simple功能简单并不完善,仅仅实现了pdf简单展示,跟为高级功能我们接着往下看。
2、PdfViewPager
首先添加项目依赖
compile 'es.voghdev.pdfviewpager:library:0.2.1'
读写网络权限肯定需要的(本地pdf不需要网络),如果是网络pdf文件需要通过RemotePDFViewPager现在远程文件,DownloadFile.Listener回调下载结果,下载成功了需要为RemotePDFViewPager绑定适配器PDFPagerAdapter,在Activity销毁时需要释放PDFPagerAdapter
public class RemotePDFActivity extends AppCompatActivity implements DownloadFile.Listener {
public void initPdfView(){
RemotePDFViewPager remotePDFViewPager =
new RemotePDFViewPager(context, "http://partners.adobe.com/public/developer/en/xml/AdobeXMLFormsSamples.pdf", this);
}
@Override
public void onSuccess(String url, String destinationPath) {
// That's the positive case. PDF Download went fine
adapter = new PDFPagerAdapter(this, "AdobeXMLFormsSamples.pdf");
remotePDFViewPager.setAdapter(adapter);
setContentView(remotePDFViewPager);
}
@Override
public void onFailure(Exception e) {
// This will be called if download fails
}
@Override
public void onProgressUpdate(int progress, int total) {
// You will get download progress here
// Always on UI Thread so feel free to update your views here
}
@Override
protected void onDestroy() {
super.onDestroy();
adapter.close();
}
}
根据github上面资料介绍,该库支持网络pdf、assets文件下的pdf、SDcard缓存的pdf文件,看上去挺不错的,但是,但是还是有版本要求
3、android-pdfview
持多种方式加载pdf,向下兼容到API8,Configurator提供了builder的方式配置属性,让代码更简洁了,使用时Gradle依赖
compile 'com.joanzapata.pdfview:android-pdfview:1.0.4@aar'
xml布局
<com.joanzapata.pdfview.PDFView
android:id="@+id/pdfview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
代码调用实例
pdfView.fromAsset(pdfName)
.pages(0, 2, 1, 3, 3, 3)
.defaultPage(1)
.showMinimap(false)
.enableSwipe(true)
.onDraw(onDrawListener)
.onLoad(onLoadCompleteListener)
.onPageChange(onPageChangeListener)
.load();
pages和onDraw是可选项,pages:它可以让你过滤和你需要的PDF页面顺序,onDraw:允许您在当前页面画布上画上一个的东西
但是,在使用上面这个库时以为就ok了,但是pdf有多种标准(按理说是统一了的SO/DIS19005-1),但在我使用中确实有些不行,不过上面这个库已经满足了大部分的需求。
3、AndroidPdfViewer
这个处理掉了上面哪个库部分格式不支持导致崩溃的问题,也是我现在在使用的库,效果图如下:
工程于API 11及更高版本。根据Apache许可证授权2.0。
加入的build.gradle:
compile 'com.github.barteksc:android-pdf-viewer:2.1.0'
PDFView在布局
<com.github.barteksc.pdfviewer.PDFView
android:id="@+id/pdfView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
加载PDF文件
pdfView.fromUri(Uri)
or
pdfView.fromFile(File)
or
pdfView.fromAsset(String)
.pages(0, 2, 1, 3, 3, 3) // all pages are displayed by default
.enableSwipe(true)
.swipeHorizontal(false)
.enableDoubletap(true)
.defaultPage(0)
.onDraw(onDrawListener)
.onLoad(onLoadCompleteListener)
.onPageChange(onPageChangeListener)
.onPageScroll(onPageScrollListener)
.onError(onErrorListener)
.enableAnnotationRendering(false)
.password(null)
.scrollHandle(null)
.load();
enableSwipe 是可选的,它可以让你改变阻止使用刷卡页面
pages 是可选的,它可以让你筛选并定向PDF的页面,看你需要
onDraw 也是可选的,并允许你画的东西提供的画布上,在当前页面上方
源码地址:https://github.com/barteksc/AndroidPdfViewer
但是就是有点大,一集成,包就增大16MB.Android的PDF查看器取决于PdfiumAndroid,这是许多体系的设置本机库(将近16 MB)的。APK必须包含所有此库的每一个市场可用的设备上运行。幸运的是,谷歌Play可让我们上传多个APK,每每建筑如之一。有自动拆分您的应用程序分成多个APK。