如何在Xamarin.Android中使用MuPDF库实现PDF页面垂直滑动
在Xamarin.Android中用MuPDF实现PDF页面垂直滑动
嘿,我之前在Xamarin.Android项目里折腾过MuPDF的垂直滑动功能,踩了几个小坑,现在把完整的实现流程分享给你:
1. 先把MuPDF集成到项目中
首先得让你的Xamarin.Android项目能用上MuPDF:
- 你可以找现成的Xamarin绑定包(注意版本和项目的兼容性),或者直接把MuPDF的Android原生库(
.so文件和aar包)导入到项目的lib目录下,配置好项目的Android原生库引用。 - 如果要加载本地PDF文件,记得在
AndroidManifest.xml里添加必要权限:
Android 6.0及以上版本还要做动态权限申请,不然会读取失败。<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
2. 实现垂直滑动的两种方案
MuPDF默认是单页显示,要实现垂直滑动,有两种常用思路,按需选择:
方案一:ScrollView包裹页面渲染View(适合小文档)
这种方式简单直接,把每个PDF页面渲染成独立的View,再放到垂直LinearLayout里,最后用ScrollView包裹实现滑动:
首先自定义页面渲染View:
public class MuPDFPageRenderer : View { private MuPDFCore _pdfCore; private int _pageNum; private Paint _renderPaint; private Rect _clipRect = new Rect(); public MuPDFPageRenderer(Context context, MuPDFCore core, int pageNum) : base(context) { _pdfCore = core; _pageNum = pageNum; _renderPaint = new Paint { AntiAlias = true, FilterBitmap = true }; } protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 根据屏幕宽度缩放页面,保证页面铺满宽度 float pageWidth = _pdfCore.GetPageWidth(_pageNum); float scale = MeasureSpec.GetSize(widthMeasureSpec) / pageWidth; int measuredHeight = (int)(_pdfCore.GetPageHeight(_pageNum) * scale); SetMeasuredDimension(MeasureSpec.GetSize(widthMeasureSpec), measuredHeight); } protected override void OnDraw(Canvas canvas) { base.OnDraw(canvas); canvas.GetClipBounds(_clipRect); // 把PDF页面渲染到当前View的画布上 _pdfCore.DrawPage(canvas, _pageNum, _clipRect, _renderPaint); } }
然后在Activity中初始化:
protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); SetContentView(Resource.Layout.Main); // 替换为你的PDF文件路径 string pdfFilePath = "/storage/emulated/0/test.pdf"; MuPDFCore pdfCore = new MuPDFCore(this, pdfFilePath); var scrollView = FindViewById<ScrollView>(Resource.Id.scrollView); var linearLayout = new LinearLayout(this) { Orientation = Orientation.Vertical }; // 把所有页面的渲染View添加到LinearLayout for (int i = 0; i < pdfCore.CountPages(); i++) { var pageView = new MuPDFPageRenderer(this, pdfCore, i); linearLayout.AddView(pageView); } scrollView.AddView(linearLayout); }
方案二:自定义可复用的滑动ViewGroup(适合大文档)
如果PDF页数很多,方案一容易内存溢出,这时候推荐自定义ViewGroup,只渲染当前可见的页面,回收不可见页面:
public class MuPDFScrollContainer : ViewGroup { private MuPDFCore _pdfCore; private List<MuPDFPageRenderer> _pageViews = new List<MuPDFPageRenderer>(); private int _firstVisiblePage = 0; private float _pageScale; public MuPDFScrollContainer(Context context, MuPDFCore core) : base(context) { _pdfCore = core; // 按屏幕宽度计算缩放比例 _pageScale = Resources.DisplayMetrics.WidthPixels / _pdfCore.GetPageWidth(0); } protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 计算所有页面总高度 int totalHeight = 0; for (int i = 0; i < _pdfCore.CountPages(); i++) { totalHeight += (int)(_pdfCore.GetPageHeight(i) * _pageScale); } SetMeasuredDimension(MeasureSpec.GetSize(widthMeasureSpec), totalHeight); } protected override void OnLayout(bool changed, int l, int t, int r, int b) { int currentTop = 0; for (int i = 0; i < _pdfCore.CountPages(); i++) { float pageHeight = _pdfCore.GetPageHeight(i) * _pageScale; // 只渲染当前可见页和前后各一页,减少内存占用 if (i >= _firstVisiblePage && i <= _firstVisiblePage + 2) { MuPDFPageRenderer pageView; if (i >= _pageViews.Count) { pageView = new MuPDFPageRenderer(Context, _pdfCore, i); _pageViews.Add(pageView); AddView(pageView); } else { pageView = _pageViews[i]; } pageView.Layout(l, currentTop, r, currentTop + (int)pageHeight); } else { // 移除不可见页面,回收内存 if (i < _pageViews.Count) { RemoveView(_pageViews[i]); } } currentTop += (int)pageHeight; } } protected override void OnScrollChanged(int l, int t, int oldl, int oldt) { base.OnScrollChanged(l, t, oldl, oldt); // 计算当前可见的第一页,触发重新布局 int scrollTop = t; int accumulatedHeight = 0; for (int i = 0; i < _pdfCore.CountPages(); i++) { float pageHeight = _pdfCore.GetPageHeight(i) * _pageScale; if (scrollTop >= accumulatedHeight && scrollTop < accumulatedHeight + pageHeight) { _firstVisiblePage = i; RequestLayout(); break; } accumulatedHeight += (int)pageHeight; } } }
在Activity中使用这个自定义容器:
protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); string pdfFilePath = "/storage/emulated/0/large_document.pdf"; MuPDFCore pdfCore = new MuPDFCore(this, pdfFilePath); var scrollContainer = new MuPDFScrollContainer(this, pdfCore); SetContentView(scrollContainer); }
3. 几个关键注意事项
- 内存管控:大文档一定要用方案二,避免因加载过多页面导致OOM。
- 错误处理:初始化MuPDFCore时要捕获异常,比如文件不存在、PDF损坏的情况。
- 缩放扩展:如果需要支持手势缩放,可以给渲染View添加触摸事件处理,调整渲染的缩放比例。
内容的提问来源于stack exchange,提问作者Amit Malhi
相关产品推荐
相关产品推荐

