首页 文章

使用Xamarin.Forms访问摄像头

提问于
浏览
30

是否有人能够提供一个关于如何使用Xamarin.Forms 1.3.x访问相机的简短,自包含的示例?只需调用本机相机应用程序并检索生成的图片就会很棒 . 在Xamarin.Forms页面上显示实时视图真棒!

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

4 回答

  • 1

    我终于为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,
               },
           },
       };
    }
    

    按钮的单击处理程序调用事件 ShouldTakePicture . 它是一个公共成员,特定于平台的代码部分稍后将分配给它 .

    public event Action ShouldTakePicture = () => {};
    

    最后,我们提供了一种显示捕获图像的公共方法:

    public void ShowImage(string filepath)
    {
        image.Source = ImageSource.FromFile(filepath);
    }
    

    Android项目

    在Android上我们修改 MainActivity . 首先,我们为捕获的图像文件定义路径:

    static readonly File file = new File(Environment.GetExternalStoragePublicDirectory(Environment.DirectoryPictures), "tmp.jpg");
    

    OnCreate 的末尾,我们可以使用创建的 App 的静态 Instance 并分配一个匿名事件处理程序,它将启动一个新的 Intent 来捕获图像:

    App.Instance.ShouldTakePicture += () => {
       var intent = new Intent(MediaStore.ActionImageCapture);
       intent.PutExtra(MediaStore.ExtraOutput, Uri.FromFile(file));
       StartActivityForResult(intent, 0);
    };
    

    最后但同样重要的是,我们的活动必须对生成的图像做出反应 . 它只是将其文件路径推送到共享 ShowImage 方法 .

    protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
    {
       base.OnActivityResult(requestCode, resultCode, data);
       App.Instance.ShowImage(file.Path);
    }
    

    就是这样!只是不要忘记在“AndroidManifest.xml”中设置“Camera”和“WriteExternalStorage”权限!

    iOS项目

    对于iOS实现,我们创建了一个自定义渲染器 . 因此,我们添加一个新文件“CustomContentPageRenderer”并在using语句后面添加相应的程序集属性:

    [assembly:ExportRenderer(typeof(ContentPage), typeof(CustomContentPageRenderer))]
    

    CustomContentPageRenderer 继承自 PageRenderer

    public class CustomContentPageRenderer: PageRenderer
    {
        ...
    }
    

    我们覆盖 ViewDidAppear 方法并添加以下部分 .

    创建一个新的图像选择器控制器参考相机:

    var imagePicker = new UIImagePickerController { SourceType = UIImagePickerControllerSourceType.Camera };
    

    一旦 ShouldTakePicture 事件被引发,就会出现图像选择器控制器:

    App.Instance.ShouldTakePicture += () => PresentViewController(imagePicker, true, null);
    

    拍摄照片后,将其保存到 MyDocuments 文件夹并调用共享 ShowImage 方法:

    imagePicker.FinishedPickingMedia += (sender, e) => {
                var filepath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "tmp.png");
    var image = (UIImage)e.Info.ObjectForKey(new NSString("UIImagePickerControllerOriginalImage"));
                InvokeOnMainThread(() => {
                    image.AsPNG().Save(filepath, false);
                    App.Instance.ShowImage(filepath);
                });
                DismissViewController(true, null);
            };
    

    最后,我们需要处理取消图像处理过程:

    imagePicker.Canceled += (sender, e) => DismissViewController(true, null);
    
  • 5

    试试James Montemagno的MediaPlugin .

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

    将提示readme.txt并按照其中的说明进行操作 . 之后,add the following codes(根据需要)到您的共享项目 . 上述readme.txt文件中要遵循的说明如下 .

    适用于Android项目

    在您的BaseActivity或MainActivity(对于Xamarin.Forms)中添加以下代码:

    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
    {
        PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }
    

    您还必须添加一些其他配置文件以遵守新的严格模式:

    • 将以下内容添加到< application >标记内的AndroidManifest.xml中:
    <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必须设置为您的应用包名称!

    • 在Resources文件夹中添加一个名为xml的新文件夹,并添加一个名为 file_paths.xml 的新XML文件

    添加以下代码:

    <?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必须设置为您的应用包名称!

    适用于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");
    
  • 34

    这就是我需要在我的应用程序中运行异步摄像头捕获:

    在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;
        }
    }
    
  • 1

    以下是如何在Xamarin Forms跨Xamarin iOS上完成的 .

    这是一个很好的基础,但它需要首先渲染一个Page,你可以在其中为它指定UIApplication,以便为Camera / Photo Picker控制器提供UIView .

    https://stackoverflow.com/a/28299259/1941942

    便携式项目

    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;
        }
    }
    

相关问题