You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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里添加必要权限:
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
    Android 6.0及以上版本还要做动态权限申请,不然会读取失败。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:12:27