Доступ к камере с помощью Xamarin.Формы
кто-нибудь может дать краткий, самодостаточный пример о том, как получить доступ к камере с помощью Xamarin.Формы 1.3.x? Просто вызов родного приложения камеры и получение полученного изображения было бы здорово. Отображение живого представления на Xamarin.Страница форм будет потрясающей!
Я уже пытался использовать Xamarin.Мобильные и Xamarin.Формы.Labs, но я не мог получить какое-либо решение для работы на обеих платформах (сейчас сосредоточен на Android и iOS). Большинство фрагментов кода, найденных в интернете (включая stackoverflow) являются неполными, например, не показывая реализацию объекта IMediaPicker или где закрепить метод для съемки.
4 ответов
я, наконец, создал минимальное решение для iOS и Android.
общий проект
во-первых, давайте посмотрим на общий код. Для легкого взаимодействия между общей App
класс и специфичный для платформы код мы храним статический Instance
внутри public static App
:
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
мы можем использовать static Instance
созданного App
и назначьте анонимный обработчик событий, который запустит новый Intent
для захвата изображения:
App.Instance.ShouldTakePicture += () => {
var intent = new Intent(MediaStore.ActionImageCapture);
intent.PutExtra(MediaStore.ExtraOutput, Uri.FromFile(file));
StartActivityForResult(intent, 0);
};
последнее, но не менее, наша деятельность должна реагировать на полученное изображение. Он просто подтолкнет свой путь к файлу shared ShowImage
метод.
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
App.Instance.ShowImage(file.Path);
}
вот и все! Только не забудьте чтобы установить разрешение" камера "и" WriteExternalStorage "в" AndroidManifest.в XML"!
проект 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);
попробуйте Джеймс Монтеманьо MediaPlugin.
вы можете установить плагин с помощью Консоль Диспетчера Пакетов, просто набрав и работает Install-Package Xam.Plugin.Media -Version 2.6.2
или перейти к Управление Пакетами NuGet... и типа Xam.Plugin.Media
и установить плагин. (Плагины должны быть установлены во всех ваших проектах, включая проекты клиентов)
A readme.txt будет предложено и следовать инструкциям. После это,добавить следующий код (при необходимости) к вашему общему проекту. Инструкции, которым следует следовать в приведенном выше readme.txt файл выглядит следующим образом.
Для Проекта Android
в вашей BaseActivity или MainActivity (для Xamarin.Формы) добавьте этот код:
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
вы также должны добавить несколько дополнительных файлов конфигурации, чтобы придерживаться нового строгого режима:
-
добавьте к вашему 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 должен быть установлен на имя пакета приложения!
-
добавьте новую папку с именем xml в папку ресурсов и добавьте новый XML-файл с именем
file_paths.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 Проект
ваше приложение должно иметь ключи в вашей информации.файл plist для NSCameraUsageDescription
и NSPhotoLibraryUsageDescription
для доступа к камере устройства и фото/видео библиотеке. Если вы используете видео возможности библиотеки, то вы также должны добавить 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");
вот что мне нужно, чтобы получить асинхронный захват камеры в моем приложении:
в 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;
}
}
вот как вы можете сделать это на Xamarin Forms cross Xamarin iOS.
Это хорошая база для запуска, но для этого требуется страница, в которой вы можете просто указать UIApplication для нее, чтобы предоставить 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;
}
}