【问题标题】:Opening a PDF with Xamarin Forms使用 Xamarin 表单打开 PDF
【发布时间】:2019-06-21 19:34:37
【问题描述】:

我使用 xamarin 表单为我的 Android 和 IOS 项目添加了一个 pdf 作为 AndroidAsset 和 BundleResource。

我只是希望能够使用设备默认使用的任何 pdf 查看器从任何设备打开这些文件。

基本上,我只是希望能够做类似的事情:

Device.OpenUri("file:///android_asset/filename.pdf");

但这似乎不起作用。什么都没有发生,并且永远不会提示用户打开 pdf。我不想使用任何允许 pdf 在应用程序中打开的第三方库,我只想将用户重定向到 pdf 查看器或浏览器。

有什么想法吗?

【问题讨论】:

  • Device.OpenUri 在默认浏览器中打开文件。您需要提供函数的完整文件路径,并且为了获得每个平台的完整路径,您需要在所有平台中创建依赖服务。此依赖服务需要发送文档相对于设备的完整路径。

标签: c# mobile xamarin.forms


【解决方案1】:

首先你需要一个接口类,因为你需要调用依赖服务来将你的文档传递给你应用的本地实现:

所以在您的共享代码中添加一个名为“IDocumentView.cs”的接口:

public interface IDocumentView
{
    void DocumentView(string file, string title);
}

安卓

现在在你的android项目中创建相应的实现“DocumentView.cs”:

assembly: Dependency(typeof(DocumentView))]
namespace MyApp.Droid.Services
{
public class DocumentView: IDocumentView
{
    void IDocumentView.DocumentView(string filepath, string title)
    {
        try
        {
            File file = new File(filepath);

            String mime = FileTypes.GetMimeTypeByExtension(MimeTypeMap.GetFileExtensionFromUrl(filepath));
            File extFile = new File (Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDocuments), file.Name);
            File extDir = extFile.ParentFile;
            // Copy file to external storage to allow other apps to access ist
            if (System.IO.File.Exists(extFile.AbsolutePath))
                System.IO.File.Delete(extFile.AbsolutePath);

            System.IO.File.Copy(file.AbsolutePath, extFile.AbsolutePath);
            file.AbsolutePath, extFile.AbsolutePath);
            // if copying was successful, start Intent for opening this file
            if (System.IO.File.Exists(extFile.AbsolutePath))
            {
                Intent intent = new Intent();
                intent.SetAction(Android.Content.Intent.ActionView);
                intent.SetDataAndType(Android.Net.Uri.FromFile(extFile), mime);
                MainApplication.FormsContext.StartActivityForResult(intent, 10);
            }
        }
        catch (ActivityNotFoundException anfe)
        {
            // android could not find a suitable app for this file
            var alert = new AlertDialog.Builder(MainApplication.FormsContext);
            alert.SetTitle("Error");
            alert.SetMessage("No suitable app found to open this file");
            alert.SetCancelable(false);
            alert.SetPositiveButton("Okay", (object sender, DialogClickEventArgs e) => ((AlertDialog)sender).Hide());
            alert.Show();
        }
        catch (Exception ex)
        {
            // another exception
            var alert = new AlertDialog.Builder(MainApplication.FormsContext);
            alert.SetTitle("Error");
            alert.SetMessage("Error when opening document");
            alert.SetCancelable(false);
            alert.SetPositiveButton("Okay", (object sender, DialogClickEventArgs e) => ((AlertDialog)sender).Hide());
            alert.Show();
        }
    }
}
}

请注意,MainApplication.FormsContext 是我添加到 MainApplication.cs 中的静态变量,以便能够快速访问应用的上下文。

在您的 Android 清单中,添加

在您的应用程序资源中,添加一个名为 file_paths.xml 的 xml 资源(到文件夹“xml”中),其内容如下:

<paths xmlns:android="http://schemas.android.com/apk/res/android">
   <external-files-path name="root" path="/"/>
   <external-files-path name="files" path="files" />
</paths>

您还需要确保目标设备上安装了能够处理相关文件的应用程序。 (Acrobat Reader、Word、Excel 等)。

iOS

iOS 已经内置了一个非常不错的文档预览,因此您可以简单地使用它(再次在您的 iOS 项目中创建一个名为“DocumentView.cs”的文件):

[assembly: Dependency(typeof(DocumentView))]
namespace MyApp.iOS.Services
{
public class DocumentView: IDocumentView
{
    void IDocumentView.DocumentView(string file, string title)
    {
        UIApplication.SharedApplication.InvokeOnMainThread(() =>
        {
            QLPreviewController previewController = new QLPreviewController();

            if (File.Exists(file))
            {
                previewController.DataSource = new PDFPreviewControllerDataSource(NSUrl.FromFilename(file), title);
                UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(previewController, true, null);
            }
        });
    }
}

public class PDFItem : QLPreviewItem
{
    public PDFItem(string title, NSUrl uri)
    {
        this.Title = title;
        this.Url = uri;
    }
    public string Title { get; set; }
    public NSUrl Url { get; set; }
    public override NSUrl ItemUrl { get { return Url; } }
    public override string ItemTitle { get { return Title; } }
}

public class PDFPreviewControllerDataSource : QLPreviewControllerDataSource
{
    PDFItem[] sources;

    public PDFPreviewControllerDataSource(NSUrl url, string filename)
    {
        sources = new PDFItem[1];
        sources[0] = new PDFItem(filename, url);
    }

    public override IQLPreviewItem GetPreviewItem(QLPreviewController controller, nint index)
    {
        int idx = int.Parse(index.ToString());
        if (idx < sources.Length)
            return sources.ElementAt(idx);
        return null;
    }

    public override nint PreviewItemCount(QLPreviewController controller)
    {
        return (nint)sources.Length;
    }
}
}

终于可以打电话了

DependencyService.Get<IDocumentView>().DocumentView(file.path, "Title of the view"); 

显示相关文件。

【讨论】:

  • 在 Android 上,当检查文件是否存在或尝试复制时,我不断收到找不到文件的异常。目前,我传递的文件路径类似于“file:///android_asset/name.pdf”。
  • 这行不通,因为您的文件不在正确的文件路径上,而是被编译到应用程序的资产中。更糟糕的是,另一个应用程序将无法访问它,因为不允许其他应用程序访问您应用程序的(内部)资产。所以基本上你需要通过使用 Assets.Open("name.pdf"); 来获取文件内容。并将其写入公共文件位置,以便其他应用程序可以访问该文件。
  • 啊,好的。这不是我们首先复制文件的原因吗?因此,如果文件在我的资产中,我将不得不使用 Assets.Open("name.pdf"),将其写入公共文件位置并且我不需要复制它?所有安卓手机都有的公共位置是什么?
  • Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDocuments) 应该返回一个公共目录来使用。默认情况下,您的应用程序,它的资产甚至它自己下载或生成的所有文件都放在自己的沙箱中,该沙箱对外界是封闭的(意味着同一设备上的其他应用程序)。 android 解决方案基本上会询问其他应用程序是否可以处理该文件,然后向用户显示哪些应用程序可以使用该文件。因此,必须通过将文件复制或写入到他们“允许”访问的位置来使他们可以访问该文件。
  • 我刚刚看到我的答案是缺少在清单中设置文件提供者信息。我在 android 部分的答案中更新了这一点。
猜你喜欢
  • 2021-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-02
  • 2016-01-08
  • 1970-01-01
  • 2018-11-21
  • 1970-01-01
相关资源
最近更新 更多