[英]Camera access with Xamarin.Forms
是否有人能夠提供一個關於如何使用Xamarin.Forms 1.3.x訪問相機的簡短,自包含的示例? 只需調用本機相機應用程序並檢索生成的圖片就會很棒。 在Xamarin.Forms頁面上顯示實時視圖真棒!
我已經嘗試過使用Xamarin.Mobile和Xamarin.Forms.Labs,但我無法在兩個平台上使用任何解決方案(現在專注於Android和iOS)。 Web上發現的大多數代碼片段(包括stackoverflow)都是不完整的,例如沒有顯示IMediaPicker對象的實現或者錨定拍照方法的位置。
我終於為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上,我們修改了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實現,我們創建了一個自定義渲染器。 因此,我們添加一個新文件“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);
試試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文件中要遵循的說明如下。
在您的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必須設置為您的應用包名稱!
將名為xml的新文件夾添加到Resources文件夾中,並添加一個名為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必須設置為您的應用包名稱!
您的應用程序需要在Info.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");
以下是如何在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();
}
[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;
}
}
這是我需要在我的應用程序中運行異步攝像頭捕獲所需的:
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;
}
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;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.