webentwicklung-frage-antwort-db.com.de

Kamerazugriff mit Xamarin.Forms

Kann jemand ein kurzes, in sich geschlossenes Beispiel geben, wie man mit Xamarin.Forms 1.3.x auf die Kamera zugreift? Es wäre großartig, einfach die native Kameraanwendung aufzurufen und das resultierende Bild abzurufen. Das Anzeigen einer Live-Ansicht auf der Xamarin.Forms-Seite wäre fantastisch!

Ich habe bereits versucht, Xamarin.Mobile und Xamarin.Forms.Labs zu verwenden, aber ich konnte keine Lösung für beide Plattformen finden (mit Schwerpunkt auf Android und iOS im Moment). Die meisten Code-Snippets im Web gefunden (einschließlich Stapelüberlauf) unvollständig sind, z. B. die Implementierung eines IMediaPicker-Objekts nicht anzeigen oder wo die Methode zum Aufnehmen von Bildern verankert werden soll.

30
Falko

Ich habe endlich eine Minimallösung für iOS und Android erstellt.

Das geteilte Projekt

Schauen wir uns zunächst den gemeinsam genutzten Code an. Für eine einfache Interaktion zwischen der gemeinsam genutzten Klasse App und dem plattformspezifischen Code speichern wir ein statisches Instance im public static App:

public static App Instance;

Außerdem zeigen wir ein Image an, das später mit Inhalten gefüllt wird. Also erstellen wir ein Mitglied:

readonly Image image = new Image();

Innerhalb des App -Konstruktors speichern wir den Instance und erstellen den Seiteninhalt, der ein einfacher button und der oben erwähnte image ist:

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

Der Click-Handler der Schaltfläche ruft das Ereignis ShouldTakePicture auf. Es ist ein öffentliches Mitglied und wird später von den plattformspezifischen Codeteilen zugewiesen.

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

Schließlich bieten wir eine öffentliche Methode zum Anzeigen des erfassten Bildes an:

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

Das Android Projekt

Am Android ändern wir den MainActivity. Zuerst definieren wir einen Pfad für die aufgenommene Bilddatei:

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

Am Ende von OnCreate können wir das statische Instance des erstellten App verwenden und einen anonymen Ereignishandler zuweisen, der ein neues Intent für die Erfassung startet ein Bild:

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

Last but not least muss unsere Aktivität auf das resultierende Bild reagieren. Der Dateipfad wird einfach auf die freigegebene ShowImage Methode verschoben.

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

Das ist alles! Vergessen Sie nicht, die Berechtigung "Camera" und "WriteExternalStorage" in "AndroidManifest.xml" festzulegen!

Das iOS-Projekt

Für die iOS-Implementierung erstellen wir einen benutzerdefinierten Renderer. Aus diesem Grund fügen wir eine neue Datei "CustomContentPageRenderer" hinzu und fügen das entsprechende Assembly-Attribut direkt nach den using-Anweisungen hinzu:

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

Das CustomContentPageRenderer erbt von PageRenderer:

public class CustomContentPageRenderer: PageRenderer
{
    ...
}

Wir überschreiben die ViewDidAppear -Methode und fügen die folgenden Teile hinzu.

Erstellen Sie einen neuen Bildwähler-Controller für die Kamera:

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

Präsentieren Sie den Bildauswahl-Controller, sobald das Ereignis ShouldTakePicture ausgelöst wird:

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

Speichern Sie das Bild nach der Aufnahme im Ordner MyDocuments und rufen Sie die freigegebene Methode ShowImage auf:

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

Und schließlich müssen wir den Abbruch des Bildaufnahmevorgangs erledigen:

imagePicker.Canceled += (sender, e) => DismissViewController(true, null);
34
Falko

Probieren Sie James Montemagnos MediaPlugin aus.

Sie können das Plugin mit Package Manager Console installieren, indem Sie einfach Install-Package Xam.Plugin.Media -Version 2.6.2 Eingeben und ausführen, oder gehen Sie zu NuGet-Pakete verwalten ... und geben Sie Xam.Plugin.Media und installiere das Plugin. (Die Plugins müssen in allen Ihren Projekten installiert sein - einschließlich der Client-Projekte)

A readme.txt wird dazu aufgefordert und befolgt die dortigen Anweisungen. Danach fügen Sie die folgenden Codes hinz (nach Bedarf) zu Ihrem freigegebenen Projekt. Die Anweisungen in der obigen Datei readme.txt lauten wie folgt.

Für Android Project

Fügen Sie in Ihrer BaseActivity oder MainActivity (für Xamarin.Forms) den folgenden Code hinzu:

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

Sie müssen auch einige zusätzliche Konfigurationsdateien hinzufügen, um den neuen strikten Modus einzuhalten:

  1. Fügen Sie Ihrem AndroidManifest.xml in den < application > -Tags Folgendes hinzu:

    <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 muss auf den Namen Ihres App-Pakets festgelegt sein!

  2. Fügen Sie einen neuen Ordner mit dem Namen xml in Ihren Ressourcenordner ein und fügen Sie eine neue XML-Datei mit dem Namen file_paths.xml Hinzu.

    Fügen Sie den folgenden Code hinzu:

    <?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 muss auf den Namen Ihres App-Pakets festgelegt sein!

Für iOS Project

Ihre App muss Schlüssel für NSCameraUsageDescription und NSPhotoLibraryUsageDescription in Ihrer Info.plist haben, um auf die Kamera und die Foto-/Videobibliothek des Geräts zugreifen zu können. Wenn Sie die Videofunktionen der Bibliothek verwenden, müssen Sie auch NSMicrophoneUsageDescription hinzufügen. Die Zeichenfolge, die Sie für jeden dieser Schlüssel angeben, wird dem Benutzer angezeigt, wenn er aufgefordert wird, die Berechtigung für den Zugriff auf diese Gerätefunktionen bereitzustellen.

Sowie:

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

Für geteiltes Projekt

Um einfach die Kamera zu öffnen, das Foto zu speichern und eine Warnung mit dem Dateipfad anzuzeigen, geben Sie Folgendes in das freigegebene Projekt ein.

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"); 
5
Curiousity

Hier ist, was ich brauchte, um eine asynchrone Kameraerfassung in meiner App zum Laufen zu bringen:

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

Und wenn Sie die Autorisierungsroutine benötigen, stellen Sie sicher, dass Sie auch die Kameranutzung in info.plist Ausfüllen, und hier ist die Funktion, um die Autorisierung zu erhalten:

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

In 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
noelicus

Hier ist, wie Sie es auf Xamarin Forms Xamarin iOS überqueren können.

Dies ist eine gute Grundlage für den Start, erfordert jedoch, dass zuerst eine Seite gerendert wird, in der Sie einfach die UIApplication angeben können, um die UIView for Camera/Photo Picker-Controller bereitzustellen.

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

Tragbares Projekt

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

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

iOS-Projekt

[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;
    }
}
1
pampi