我正在使用Xamarin.Forms开发移动应用程序。在我的应用程序中,我使用自定义视图实现自定义相机。我已经成功实现了自定义摄像头视图。我现在要做的是,当我点击共享项目中的按钮时,我想从特定于平台的自定义视图渲染器中检索一些数据。请参阅下面的方案。
这是共享项目中的CameraPage.xaml。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:local="clr-namespace:MementoApp.Views;"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Title="Capture your moment"
Padding="0,20,0,0"
x:Class="MementoApp.Views.CameraPage">
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<local:CameraPreview x:Name="Camera" Camera="Rear" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
<StackLayout Orientation="Vertical" HorizontalOptions="Center">
<Button x:Name="buttonCapturePhoto" Text="Capture photo" />
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
如您所见,CameraPreview是自定义视图。我还在它下方添加了一个按钮。
这是CameraPreview类
namespace MementoApp.Views
{
public enum CameraOptions
{
Rear,
Front
}
public class CameraPreview : View
{
public static readonly BindableProperty CameraProperty = BindableProperty.Create(
propertyName: "Camera",
returnType: typeof(CameraOptions),
declaringType: typeof(CameraPreview),
defaultValue: CameraOptions.Rear);
public CameraOptions Camera
{
get { return (CameraOptions)GetValue(CameraProperty); }
set { SetValue(CameraProperty, value); }
}
}
}
在Android项目中,我为相机自定义视图创建了渲染器。
[assembly: ExportRenderer(typeof(MementoApp.Views.CameraPreview), typeof(MementoApp.Droid.CameraPreviewRenderer))]
namespace MementoApp.Droid
{
public class CameraPreviewRenderer : ViewRenderer<MementoApp.Views.CameraPreview, MementoApp.Droid.CameraPreview>
{
CameraPreview cameraPreview;
public CameraPreview CameraPreview { get { return this.cameraPreview; } }
public CameraPreviewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<MementoApp.Views.CameraPreview> e)
{
base.OnElementChanged(e);
if (Control == null)
{
cameraPreview = new CameraPreview(Context);
SetNativeControl(cameraPreview);
}
if (e.OldElement != null)
{
// Unsubscribe
cameraPreview.Click -= OnCameraPreviewClicked;
}
if (e.NewElement != null)
{
Control.Preview = Camera.Open((int)e.NewElement.Camera);
// Subscribe
cameraPreview.Click += OnCameraPreviewClicked;
}
}
void OnCameraPreviewClicked(object sender, EventArgs e)
{
if (cameraPreview.IsPreviewing)
{
cameraPreview.Preview.StopPreview();
cameraPreview.IsPreviewing = false;
}
else
{
cameraPreview.Preview.StartPreview();
cameraPreview.IsPreviewing = true;
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
Control.Preview.Release();
}
base.Dispose(disposing);
}
}
}
这是Android项目中的CameraPreview类。
public sealed class CameraPreview : ViewGroup, ISurfaceHolderCallback
{
SurfaceView surfaceView;
ISurfaceHolder holder;
Camera.Size previewSize;
IList<Camera.Size> supportedPreviewSizes;
Camera camera;
IWindowManager windowManager;
public bool IsPreviewing { get; set; }
public Camera Preview
{
get { return camera; }
set
{
camera = value;
if (camera != null)
{
supportedPreviewSizes = Preview.GetParameters().SupportedPreviewSizes;
RequestLayout();
}
}
}
public CameraPreview(Context context)
: base(context)
{
surfaceView = new SurfaceView(context);
AddView(surfaceView);
windowManager = Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
IsPreviewing = false;
holder = surfaceView.Holder;
holder.AddCallback(this);
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int width = ResolveSize(SuggestedMinimumWidth, widthMeasureSpec);
int height = ResolveSize(SuggestedMinimumHeight, heightMeasureSpec);
SetMeasuredDimension(width, height);
if (supportedPreviewSizes != null)
{
previewSize = GetOptimalPreviewSize(supportedPreviewSizes, width, height);
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
var msw = MeasureSpec.MakeMeasureSpec(r - l, MeasureSpecMode.Exactly);
var msh = MeasureSpec.MakeMeasureSpec(b - t, MeasureSpecMode.Exactly);
surfaceView.Measure(msw, msh);
surfaceView.Layout(0, 0, r - l, b - t);
}
public void SurfaceCreated(ISurfaceHolder holder)
{
try
{
if (Preview != null)
{
Preview.SetPreviewDisplay(holder);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(@" ERROR: ", ex.Message);
}
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
if (Preview != null)
{
Preview.StopPreview();
}
}
public void SurfaceChanged(ISurfaceHolder holder, Android.Graphics.Format format, int width, int height)
{
var parameters = Preview.GetParameters();
parameters.SetPreviewSize(previewSize.Width, previewSize.Height);
RequestLayout();
switch (windowManager.DefaultDisplay.Rotation)
{
case SurfaceOrientation.Rotation0:
camera.SetDisplayOrientation(90);
break;
case SurfaceOrientation.Rotation90:
camera.SetDisplayOrientation(0);
break;
case SurfaceOrientation.Rotation270:
camera.SetDisplayOrientation(180);
break;
}
Preview.SetParameters(parameters);
Preview.StartPreview();
IsPreviewing = true;
}
Camera.Size GetOptimalPreviewSize(IList<Camera.Size> sizes, int w, int h)
{
const double AspectTolerance = 0.1;
double targetRatio = (double)w / h;
if (sizes == null)
{
return null;
}
Camera.Size optimalSize = null;
double minDiff = double.MaxValue;
int targetHeight = h;
foreach (Camera.Size size in sizes)
{
double ratio = (double)size.Width / size.Height;
if (Math.Abs(ratio - targetRatio) > AspectTolerance)
continue;
if (Math.Abs(size.Height - targetHeight) < minDiff)
{
optimalSize = size;
minDiff = Math.Abs(size.Height - targetHeight);
}
}
if (optimalSize == null)
{
minDiff = double.MaxValue;
foreach (Camera.Size size in sizes)
{
if (Math.Abs(size.Height - targetHeight) < minDiff)
{
optimalSize = size;
minDiff = Math.Abs(size.Height - targetHeight);
}
}
}
return optimalSize;
}
}
我现在要做的是,我想从&#34; Capture&#34;中获取来自SharedProject的CameraPreviewRenderer中的一些数据。单击按钮。我在像这样的共享项目中的CameraPage.xaml.cs中为这样的捕获按钮创建了事件。
void ButtonCapturePhoto_Clicked(object sender, EventArgs e)
{
}
在该事件中,我想从Android项目中的CameraPreviewRenderer中检索数据。例如,我在CameraPreviewRenderer类中创建了一个属性,如下所示。
public string Data { get { return "Message from the Android Project"; } }
在事件中,也许我会像这样检索那个属性。
Camera.Data //Data would be "Message from the Android Project" and Camera is the custom view.
我怎样才能做到这一点?
答案 0 :(得分:0)
Xamarin.Forms Messaging Center是一种方法。只需在Custom Renderer类中订阅该事件即可。
另一种方法是在CameraPreview类中创建Data
属性而不是CameraPreviewRenderer,然后在单击按钮时设置该属性。 IE,
void ButtonCapturePhoto_Clicked(object sender, EventArgs e)
{
Camera.Data = ...;
}
现在,您将能够在CameraPreviewRenderer类的Data
方法中获得OnElementPropertyChanged
。
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == CameraPreview.DataProperty.PropertyName)
{
... do what you need to do ...
}
}
更新:最好的方法是使用事件处理程序。在CameraPreview
类中,创建一个事件处理程序和方法来调用它:
public event eventHandler<MyDataObject> OnData;
public void InvokeOnData(MyDataObject obj)
{
OnData?.Invoke(this, obj);
}
然后在CameraPreviewRenderer
的{{1}}方法中,您可以设置事件处理程序:
OnElementChanged
然后,您可以这样调用它:
protected override void OnElementPropertyChanged(...)
{
var cameraPreview = (CameraPreview)this.Element;
cameraPreview.OnData += HandleData;
}
private void HandleData(object sender, MyCameraObject e)
{
... do what you need to do ...
}
答案 1 :(得分:0)
您可以在ButtonCapturePhoto_Clicked
方法中使用DependencyService,以下是我的演示:
在SharedProject中,定义接口IGetData
:
namespace MementoApp
{
public interface IGetData
{
string getData();
}
}
在Android项目中,实现界面:
[assembly: Xamarin.Forms.Dependency(typeof(GetDataImpl))]
namespace MementoApp.Droid
{
public class GetDataImpl :IGetData
{
public string getData()
{
//in this method, you can also use other approaches to get the data, like delegate/event/interface.
CameraPreviewRenderer cpr= new CameraPreviewRenderer(Application.Context);
cpr.initData();
return cpr.Data;
}
}
}
您的CameraPreviewRenderer
添加了这些内容(我使用方法 - initData
来模拟Data
代):
public string Data;
public void initData() {
Data= "Message from the Android Project";
}
最后,在ButtonCapturePhoto_Clicked
:
private void ButtonCapturePhoto_Clicked(object sender, EventArgs e)
{
string data = DependencyService.Get<IGetData>().getData();
System.Diagnostics.Debug.Write("data====="+data);
}