使用Xamarin.Forms访问相机

35

有没有人可以给出一个简短的、自包含的示例,说明如何在Xamarin.Forms 1.3.x中访问相机?只需调用本地相机应用程序并检索结果图片即可。在Xamarin.Forms页面上显示实时视图将会很棒!

我已经尝试使用Xamarin.Mobile和Xamarin.Forms.Labs,但我无法让任何解决方案在两个平台上运行(现在专注于Android和iOS)。大多数在网上找到的代码片段(包括stackoverflow)都是不完整的,例如未显示IMediaPicker对象的实现或拍照方法的锚定位置。

4个回答

38

我最终为iOS和Android创建了一个最小解决方案。

共享项目

首先,让我们来看看共享代码。为了实现共享的App类和特定于平台的代码之间的简单交互,我们在public static App中存储一个静态Instance

public static App Instance;

此外,我们将显示一个Image,稍后将用内容填充。因此,我们创建一个成员:

readonly Image image = new Image();

App 构造函数中,我们存储了 Instance 并创建页面内容,其中包括一个简单的 button 和前面提到的 image

public App()
{
   Instance = this;

   var button = new Button {
       Text = "Snap!",
       Command = new Command(o => ShouldTakePicture()),
   };

   MainPage = new ContentPage {
       Content = new StackLayout {
       VerticalOptions = LayoutOptions.Center,
           Children = {
                    button,
                    image,
           },
       },
   };
}

1
嘿,我需要完全相同的东西,但是仅使用上面的代码无法让它起作用,你在Github或其他地方有这个代码吗? - Norbor Illig
3
您可能希望了解这个稍微更新且简化的版本,链接在这里:http://xforms-kickstarter.com/#camera。 - Falko
谢谢,我会看一下的。 - Norbor Illig
我成功地运行了“稍微更新和简化”的版本。太棒了!非常感谢 @Falko。但我正在努力想出如何将其适应于在其他页面上显示实时视图(首先在iOS上)。有人有建议吗? - BillF
对于首页以外的页面,以下代码(iOS)有效:UIImagePickerController imagePicker = new UIImagePickerController { SourceType = UIImagePickerControllerSourceType.Camera };UIWindow window= UIApplication.SharedApplication.KeyWindow; UIViewController vc = window.RootViewController; while (vc.PresentedViewController != null) vc = vc.PresentedViewController;vc.PresentViewController(imagePicker, true, null); - BillF
显示剩余3条评论

5

尝试使用James Montemagno的MediaPlugin

您可以使用Package Manager Console安装插件,只需输入并运行Install-Package Xam.Plugin.Media -Version 2.6.2或者转到Manage NuGet Packages...并键入Xam.Plugin.Media并安装插件。(必须在所有项目中安装插件-包括客户端项目)

会提示一个readme.txt文件,请按照其中的说明进行操作。之后,根据需要将以下代码添加到共享项目中。上述readme.txt文件中应遵循以下说明。

对于Android项目

在您的BaseActivity或MainActivity(对于Xamarin.Forms)中添加以下代码:
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
    PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}  

您还需要添加一些额外的配置文件以遵循新的严格模式:

  1. Add the following to your AndroidManifest.xml inside the <application> tags:

    <provider android:name="android.support.v4.content.FileProvider" 
              android:authorities="YOUR_APP_PACKAGE_NAME.fileprovider" 
              android:exported="false" 
              android:grantUriPermissions="true">
        <meta-data android:name="android.support.FILE_PROVIDER_PATHS" 
                   android:resource="@xml/file_paths"></meta-data>
    </provider>
    

    YOUR_APP_PACKAGE_NAME must be set to your app package name!

  2. Add a new folder called xml into your Resources folder and add a new XML file called file_paths.xml

    Add the following code:

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-path name="my_images" path="Android/data/YOUR_APP_PACKAGE_NAME/files/Pictures" />
        <external-path name="my_movies" path="Android/data/YOUR_APP_PACKAGE_NAME/files/Movies" />
    </paths>
    

    YOUR_APP_PACKAGE_NAME must be set to your app package name!

iOS项目

您的应用程序需要在Info.plist文件中添加NSCameraUsageDescriptionNSPhotoLibraryUsageDescription密钥,以便访问设备的相机和照片/视频库。如果您使用库的视频功能,则还必须添加NSMicrophoneUsageDescription。当用户被提示提供访问这些设备功能的权限时,您为每个密钥提供的字符串将显示给用户。

例如:

<key>NSCameraUsageDescription</key>
<string>This app needs access to the camera to take photos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to photos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone.</string>

针对共享项目

为了简单地打开相机、保存照片并显示一个带有文件路径的警告,将以下内容输入到共享项目中。

if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
    await DisplayAlert("No Camera", ":( No camera avaialble.", "OK");
    return;
}

var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
    PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium,
    Directory = "Sample",
    Name = "test.jpg"
});

if (file == null)
    return;

await DisplayAlert("File Location", file.Path, "OK"); 

您好,我想请问如何在相机启动时自动激活闪光灯? - Shi Jie Tio
你好,对于媒体插件,有一个默认的相机,即 DefaultCamera = Plugin.Media.Abstractions.CameraDevice.Front如果我想让闪光灯始终开启,那么是否可以像默认相机一样设置默认功能? - Shi Jie Tio

1
这是如何在Xamarin Forms跨Xamarin iOS上实现的。
这是一个很好的起点,但需要先呈现一个页面,您可以在其中指定UIApplication,以便为相机/照片选择器控制器提供UIView。

https://dev59.com/El4c5IYBdhLWcg3wMn31#28299259

便携式项目

public interface ICameraProvider
{
    Task<CameraResult> TakePhotoAsync();
    Task<CameraResult> PickPhotoAsync();
}

private Command AttachImage 
{
    var camera = await DependencyService.Get<ICameraProvider>().TakePhotoAsync();
}

iOS 项目
[assembly: Xamarin.Forms.Dependency(typeof(CameraProvider))]

public class CameraProvider : ICameraProvider
{
    private UIImagePickerController _imagePicker;
    private CameraResult _result;
    private static TaskCompletionSource<CameraResult> _tcs;

    public async Task<CameraResult> TakePhotoAsync()
    {
        _tcs = new TaskCompletionSource<CameraResult>();

        _imagePicker = new UIImagePickerController { SourceType = UIImagePickerControllerSourceType.Camera };

        _imagePicker.FinishedPickingMedia += (sender, e) =>
        {
            _result = new CameraResult();
            var filepath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "tmp.png");
            var image = (UIImage)e.Info.ObjectForKey(new NSString("UIImagePickerControllerOriginalImage"));

            _result.ImageSource = ImageSource.FromStream(() => new MemoryStream(image.AsPNG().ToArray())); 
            _result.ImageBytes = image.AsPNG().ToArray();
            _result.FilePath = filepath;

            _tcs.TrySetResult(_result);
            _imagePicker.DismissViewController(true, null);
        };

        _imagePicker.Canceled += (sender, e) =>
        {
            UIApplication.SharedApplication.KeyWindow.RootViewController.DismissViewController(true, null);
        };

        await UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewControllerAsync(_imagePicker, true);

        return await _tcs.Task; 
    }

    public async Task<CameraResult> PickPhotoAsync()
    {
        _tcs = new TaskCompletionSource<CameraResult>();

        _imagePicker = new UIImagePickerController
        {
            SourceType = UIImagePickerControllerSourceType.PhotoLibrary,
            MediaTypes = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary)
        };

        _imagePicker.FinishedPickingMedia += (sender, e) =>
        {

            if (e.Info[UIImagePickerController.MediaType].ToString() == "public.image")
            {
                var filepath = (e.Info[new NSString("UIImagePickerControllerReferenceUrl")] as NSUrl);
                var image = (UIImage)e.Info.ObjectForKey(new NSString("UIImagePickerControllerOriginalImage"));
                //var image = e.Info[UIImagePickerController.OriginalImage] as UIImage;

                _result.ImageSource = ImageSource.FromStream(() => new MemoryStream(image.AsPNG().ToArray()));
                _result.ImageBytes = image.AsPNG().ToArray();
                _result.FilePath = filepath?.Path;
            }

            _tcs.TrySetResult(_result);
            _imagePicker.DismissViewController(true, null);
        };

        _imagePicker.Canceled += (sender, e) =>
        {
            UIApplication.SharedApplication.KeyWindow.RootViewController.DismissViewController(true, null);
        };

        await UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewControllerAsync(_imagePicker, true);

        return await _tcs.Task;
    }
}

0
这是我在我的应用程序中实现异步相机捕获所需的内容:
在iOS中:
public async Task<string> TakePicture()
{
    if (await AuthorizeCameraUse())
    {
        var imagePicker = new UIImagePickerController { SourceType = UIImagePickerControllerSourceType.Camera };

        TaskCompletionSource<string> FinishedCamera = new TaskCompletionSource<string>();

        // When user has taken picture
        imagePicker.FinishedPickingMedia += (sender, e) => {
            // Save the file
            var filepath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "tmp.png");
            var image = (UIImage)e.Info.ObjectForKey(new NSString("UIImagePickerControllerOriginalImage"));
            image.AsPNG().Save(filepath, false);

            // Close the window
            UIApplication.SharedApplication.KeyWindow.RootViewController.DismissViewController(true, null);

            // Stop awaiting
            FinishedCamera.SetResult(filepath);
        };

        // When user clicks cancel 
        imagePicker.Canceled += (sender, e) =>
        {
            UIApplication.SharedApplication.KeyWindow.RootViewController.DismissViewController(true, null);
            FinishedCamera.TrySetCanceled();
        };

        // Show the camera-capture window
        UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(imagePicker, true, null);

        // Now await for the task to complete or be cancelled
        try
        {
            // Return the path we've saved the image in
            return await FinishedCamera.Task;
        }
        catch (TaskCanceledException)
        {
            // handle if the user clicks cancel
        }
    }

    return null;
}

如果您需要授权程序,请确保您也在info.plist中填写了相机使用信息,这里是获取授权的函数:

public static async Task<bool> AuthorizeCameraUse()
{
    var authorizationStatus = AVCaptureDevice.GetAuthorizationStatus(AVMediaType.Video);

    if (authorizationStatus != AVAuthorizationStatus.Authorized)
    {
        return await AVCaptureDevice.RequestAccessForMediaTypeAsync(AVMediaType.Video);
    }
    else
        return true;
}

在Android中:

private TaskCompletionSource<bool> _tcs_NativeCamera;

public async Task<string> TakePicture()
{
    _tcs_NativeCamera = new TaskCompletionSource<bool>();

    // Launch the camera activity
    var intent = new Intent(MediaStore.ActionImageCapture);
    intent.PutExtra(MediaStore.ExtraOutput, Android.Net.Uri.FromFile(cameraCaptureFilePath));

    NextCaptureType = stype;

    StartActivityForResult(intent, SCAN_NATIVE_CAMERA_CAPTURE_ASYNC);

    // Wait here for the activity return (through OnActivityResult)
    var Result = await _tcs_NativeCamera.Task;

    // Return the camera capture file path
    return Result != Result.Canceled ? cameraCaptureFilePath : null;
}

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);

    switch (requestCode)
    {
        case SCAN_NATIVE_CAMERA_CAPTURE_ASYNC:
            _tcs_NativeCamera.SetResult(resultCode);
            break;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接