上一篇文章,我们介绍了如何去实现读取手机文件及文件夹,并以列表的形式显示出来,今天我们将说说如何读取pdf文件。先看效果图:
选择pdf文件 显示pdf文件主界面
一、实现原理:
手机端解析pdf的实例并不是很多,android系统本身没有解析的类,就需要第三方开发库,现在常用的两种方式:一种是利用动态链接库libmupdf.so;另一种是利用动态链接库libvudroid.so。两者各有自己的优缺点,前者解析速度快,但是比较耗内存;后者省内存,但是解析比较慢。本项目是利用前者动态库来解析pdf的,这两个在网上都可以下载,也有一个相关的实例。本人感觉,前者比较简单,后者实现的相对复杂,之前有人说前者动态库是在后者的基础上写的,等待求证。下面我们就介绍libmupdf.so动态库:MUPDF是一个轻量级pdf显示阅读器,大家需要把limupdf.so导入到项目中,建立一个包名为com.artifex.mupdf,目录结构如下:
在com.artifex.mupdf包中新建这几个类,我们重点介绍MuPDFActivity、ReaderView、PageView、MuPDFCore。
在MuPDFActivity类中获取要读取的pdf地址,传给MuPDFCore对象:
/**
* 功能:根据获取的文件路径,解析pdf,并返回core
* @param path
* @return core
*/
private MuPDFCore openFile(String path)
{
PATH = path;
int lastSlashPos = path.lastIndexOf('/');
mFileName = new String(lastSlashPos == -1
? path
: path.substring(lastSlashPos+1));
System.out.println("Trying to open "+path);
try
{
core = new MuPDFCore(path);
// New file: drop the old outline data
}
catch (Exception e)
{
System.out.println(e);
return null;
}
return core;
}
MuPDFCore开始解析pdf,最后将其实例传给ReaderView:
private ReaderView mDocView;
mDocView = new ReaderView(this) {
private boolean showButtonsDisabled;
public boolean onSingleTapUp(MotionEvent e) {
if (e.getX() < super.getWidth()/TAP_PAGE_MARGIN) {
super.moveToPrevious();
} else if (e.getX() > super.getWidth()*(TAP_PAGE_MARGIN-1)/TAP_PAGE_MARGIN) {
super.moveToNext();
} else if (!showButtonsDisabled) {
int linkPage = -1;
if (mLinkState != LinkState.INHIBIT) {
MuPDFPageView pageView = (MuPDFPageView) mDocView.getDisplayedView();
if (pageView != null) {
// XXX linkPage = pageView.hitLinkPage(e.getX(), e.getY());
}
}
if (linkPage != -1) {
mDocView.setDisplayedViewIndex(linkPage);
} else {
if (!mButtonsVisible) {
showButtons();
} else {
hideButtons();
}
}
}
return super.onSingleTapUp(e);
}
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (!showButtonsDisabled)
hideButtons();
return super.onScroll(e1, e2, distanceX, distanceY);
}
public boolean onScaleBegin(ScaleGestureDetector d) {
showButtonsDisabled = true;
return super.onScaleBegin(d);
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN){
showButtonsDisabled = false;
}
return super.onTouchEvent(event);
}
protected void onChildSetup(int i, View v) {
((PageView)v).setLinkHighlighting(mLinkState == LinkState.HIGHLIGHT);
}
protected void onMoveToChild(int i) {
if (core == null)
return;
mPageNumberView.setText(String.format("%d/%d", i+1, core.countPages()));
mPageSlider.setMax(core.countPages()-1);
mPageSlider.setProgress(i);
}
protected void onSettle(View v) {
// When the layout has settled ask the page to render
// in HQ
((PageView)v).addHq();
}
protected void onUnsettle(View v) {
// When something changes making the previous settled view
// no longer appropriate, tell the page to remove HQ
((PageView)v).removeHq();
}
};
mDocView.setAdapter(new MuPDFPageAdapter(this, core));
ReaderView获取pdf的页面信息后传给PageView,这样pdf文件就显示出来了,其原理就是将每一页转成一个bitmap对象:
MuPDFPageView pageView = (MuPDFPageView) mDocView.getDisplayedView();
ReaderView包含了对pdf文件的相关操作,比如页面放大缩小、翻页等相关手势:
/**
* 用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发
* */
public boolean onDown(MotionEvent arg0) {
mScroller.forceFinished(true);
//Log.e("info", "-->onDown");
return true;
}
/*
* 用户按下触摸屏、快速移动后松开,
* 由1个MotionEvent ACTION_DOWN,
* 多个ACTION_MOVE, 1个ACTION_UP触发
* */
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if (mScrollDisabled)
return true;
View v = mChildViews.get(mCurrent);
if (v != null) {
Rect bounds = getScrollBounds(v);
switch(directionOfTravel(velocityX, velocityY)) {
case MOVING_LEFT:
if (bounds.left >= 0) {
// Fling off to the left bring next view onto screen
View vl = mChildViews.get(mCurrent+1);
if (vl != null) {
slideViewOntoScreen(vl);
return true;
}
}
break;
case MOVING_RIGHT:
if (bounds.right <= 0) {
// Fling off to the right bring previous view onto screen
View vr = mChildViews.get(mCurrent-1);
if (vr != null) {
slideViewOntoScreen(vr);
return true;
}
}
break;
}
mScrollerLastX = mScrollerLastY = 0;
Rect expandedBounds = new Rect(bounds);
expandedBounds.inset(-FLING_MARGIN, -FLING_MARGIN);
if(withinBoundsInDirectionOfTravel(bounds, velocityX, velocityY)
&& expandedBounds.contains(0, 0)) {
mScroller.fling(0, 0, (int)velocityX, (int)velocityY, bounds.left, bounds.right, bounds.top, bounds.bottom);
post(this);
}
}
return true;
}
/**
* 用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发
* */
public void onLongPress(MotionEvent e) {
}
/**
* 功能:扑捉屏幕手势滑动动作
* 用户按下触摸屏,并拖动,由1个MotionEvent ACTION_DOWN,
* 多个ACTION_MOVE触发。
* */
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
//Log.e("info", "-->onScroll");
//这里控制pdf文件翻页
if (!mScrollDisabled) {
mXScroll -= distanceX;
mYScroll -= distanceY;
requestLayout();
}
return true;
}
/**
* 用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发
* 注意和onDown()的区别,强调的是没有松开或者拖动的状态 .
* */
public void onShowPress(MotionEvent e) {
//Log.e("info", "-->onShowPress");
}
/**
* 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发
* */
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
/**
* 处理对屏幕的缩放比例
* */
public boolean onScale(ScaleGestureDetector detector) {
//截屏视图不显示时,手势操作可以进行
float previousScale = mScale;
mScale = Math.min(Math.max(mScale * detector.getScaleFactor(), MIN_SCALE), MAX_SCALE);
scalingFactor = mScale/previousScale;//缩放比例
//Log.e("info", "--->scalingFactor="+scalingFactor);
View v = mChildViews.get(mCurrent);
if (v != null) {
// Work out the focus point relative to the view top left
int viewFocusX = (int)detector.getFocusX() - (v.getLeft() + mXScroll);
int viewFocusY = (int)detector.getFocusY() - (v.getTop() + mYScroll);
// Scroll to maintain the focus point
mXScroll += viewFocusX - viewFocusX * scalingFactor;
mYScroll += viewFocusY - viewFocusY * scalingFactor;
requestLayout();
}
return true;
}
public boolean onScaleBegin(ScaleGestureDetector detector) {
mScaling = true;
// Ignore any scroll amounts yet to be accounted for: the
// screen is not showing the effect of them, so they can
// only confuse the user
mXScroll = mYScroll = 0;
// Avoid jump at end of scaling by disabling scrolling
// until the next start of gesture
mScrollDisabled = true;
return true;//一定要返回true才会进入onScale()这个函数
}
public void onScaleEnd(ScaleGestureDetector detector) {
mScaling = false;
}
/*
* 在onTouch()方法中,我们调用GestureDetector的onTouchEvent()方法,将捕捉到的MotionEvent交给GestureDetector
* 来分析是否有合适的callback函数来处理用户的手势
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return mGestureDetector.onTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mScaleGestureDetector.onTouchEvent(event);
if (!mScaling)
mGestureDetector.onTouchEvent(event);
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mUserInteracting = true;
}
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
mScrollDisabled = false;
mUserInteracting = false;
if(event.getAction() == MotionEvent.ACTION_UP){
View v = mChildViews.get(mCurrent);
if (v != null) {
if (mScroller.isFinished()) {
slideViewOntoScreen(v);
}
if (mScroller.isFinished()) {
postSettle(v);
}
}
}
}
requestLayout();
return true;
}
这里我们说到了手势的相关操作,手势操作是比较流行的,主要是要实现OnGestureListener接口,例如Fling,Scroll等等。这些Gesture会使用户体验大大提升。
这些完成之后,我们就要在FileActivity类中调用MuPDFActivity:
Intent intent = new Intent(getApplicationContext(), MuPDFActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.setData(uri);
startActivity(intent);
最后要在清单文件中注册相应的activity:
<activity android:name="com.artifex.mupdf.MuPDFActivity"
android:label="@string/app_name"></activity>
到此我们就可以显示要读取的pdf文件了,并可以对页面就行放大缩小、翻页操作。这个也是从一个开源项目中学到,根据自己的需求实现了其中一部分功能,MuPDF解析pdf远比这些功能要多,这只是一小部分而已。对于具体实现过程,大家可以下载本博客相应的实例源码,这里就不全部贴出来了,在最后边将把代码下载链接留给大家,需要的朋友可以下载看看。明天我们将会介绍如何实现手写画板。欢迎大家继续关注,对于不足之处敬请指正,大家一起进步,上传资源已经运行,没有问题,如果你不能运行,请在博客留言……
【android开发】手写签名系统的设计与实现之实现解析pdf文件(二)