OnElementChanged(...) not getting the new value of property - android

I have a a map that displays a route between points. This is working but now I want to pass some color values from PCL. I have a custom renderer for android and a class acting as bridge between PCL and android with bindable properties in it.
(For the map I use the PCL implementation and extends it with the custom renderer)
For now I got:
(android : CustomMapRenderer)
[assembly: ExportRenderer(typeof(CustomMap), typeof(MapOverlay.Droid.CustomMapRenderer))]
namespace MapOverlay.Droid
{
public class CustomMapRenderer : MapRenderer , ICustomMap
{
List<Position> routeCoordinates;
Int32 RouteColor;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// Unsubscribe
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
routeCoordinates = formsMap.RouteCoordinates;
Control.GetMapAsync(this);
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (this.Element == null || this.Control == null)
return;
if (e.PropertyName == "VisibleRegion")
{
var polylineOptions = new PolylineOptions();
polylineOptions.InvokeColor(0x66FF0000);
foreach (var position in routeCoordinates)
{
polylineOptions.Add(new LatLng(position.Latitude, position.Longitude));
}
NativeMap.AddPolyline(polylineOptions);
}
}
public void working()
{
Log.Debug("xxxx", "WORKING");
}
}
}
Now the class acting as bridge from PCL to Android
(PCL : CustomMap)
namespace SomeNamespace
{
public class CustomMap : Map
{
public static readonly BindableProperty RouteCoordinatesProperty =
BindableProperty.Create<CustomMap, List<Position>>(p => p.RouteCoordinates, new List<Position>());
public static readonly BindableProperty RouteColorProperty =
BindableProperty.Create<CustomMap, int>(p => p.RouteColor, 0x66FF0000);
//Property used to add points to the map. Then polyline utility will draw a line beteween thoses points
public List<Position> RouteCoordinates
{
get { return (List<Position>)base.GetValue(RouteCoordinatesProperty); }
set {
base.SetValue(RouteCoordinatesProperty, value);
}
}
public Int32 RouteColor
{
get { return (Int32)base.GetValue(RouteColorProperty); }
set { base.SetValue(RouteColorProperty, value); }
}
public CustomMap()
{
RouteCoordinates = new List<Position>();
}
}
}
So as you can see I added an Int32 RouteColor variable wich should be used in the CustomMapRenderer to change the color of the displayed route.
But when I change this value in my PCL code like so :
mCustomMap.RouteColor = 0x22AA0000;
It triggers an OnElementPropertyChanged response but not an OnElementChanged event.
So I can't get the changed value. I only know that it DID changed (with the OnElementPropertyChanged).
If someone knows how to bypass this phenomena... All suggestions are welcome ;-)
Thanks in advance !

Element represents the forms element, which in this case should be CustomMap. You can use it to retrieve the property values.
For e.g.:
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (this.Element == null || this.Control == null)
return;
....
//if RouteColor is the property that changed
if (e.PropertyName == nameof(CustomMap.RouteColor))
{
//get new value
var newColor = (Element as CustomMap)?.RouteColor;
//and, update native control
....

Related

Event inside a custom renderer not firing

I'm making a xamarin.form app,
I have a custom render for streaming the camera inside a view , I've followed the tutorial here:
camera stream tutorial
I've managed to correctly show the camera stream inside a view, now I'm trying to create a button that toggle the flash:
here is my custom renderer:
using System;
using System.Diagnostics;
using Hangover.Camera.Factory;
using Hangover.GUIPersonalizzata;
using Xamarin.Forms;
//Questa classe permette di visualizzare lo stream della fotocamera
namespace Hangover.Camera.Views
{
public class CameraStream:View
{
public static readonly BindableProperty CameraProperty = BindableProperty.Create(
propertyName: "Camera",
returnType: typeof(CameraOptions),
declaringType: typeof(CameraStream),
defaultValue: CameraOptions.Rear);
//Funzioni Fotocamera
private Fotocamera _fotocamera;
public event EventHandler flash_event;
//costruttore di convenienza
public CameraStream()
{
}
public CameraStream(Fotocamera fotocamera){
_fotocamera = fotocamera;
}
public CameraOptions Camera
{
get { return (CameraOptions)GetValue(CameraProperty); }
set { SetValue(CameraProperty, value); }
}
public void gestisciFlash(BottoneSwitch btn){
if (flash_event != null)
flash_event.Invoke(btn, null);
}
}
}
and here is the iOS implementation:
using System;
using System.Diagnostics;
using Hangover.Camera.Factory;
using Hangover.Camera.Views;
using Hangover.GUIPersonalizzata;
using Hangover.iOS.CustomRenderer;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(CameraStream), typeof(CameraStreamRenderer))]
namespace Hangover.iOS.CustomRenderer
{
public class CameraStreamRenderer : ViewRenderer<CameraStream, UICameraPreview>,ICameraFunctions
{
UICameraPreview uiCameraPreview; // fotocamera nativa
protected override void OnElementChanged(ElementChangedEventArgs<CameraStream> elem)
{
base.OnElementChanged(elem);
if (Control == null)
{
uiCameraPreview = new UICameraPreview(elem.NewElement.Camera);
SetNativeControl(uiCameraPreview);
var camera_stream = (CameraStream)elem.NewElement;
camera_stream.flash_event += (object sender, EventArgs e) => {
Debug.Write("teeeest");
};
}
}
//Invocato quando la view viene chiusa
protected override void Dispose(bool disposing)
{
if (disposing)
{
Control.CaptureSession.Dispose();
Control.Dispose();
}
base.Dispose(disposing);
}
public void attivaFlash()
{
Debug.Write("flash");
Debug.Write("flash");
Debug.Write("flash");
}
public void distattivaFlash()
{
throw new NotImplementedException();
}
public void scattaFoto()
{
throw new NotImplementedException();
}
public void ruotaFotocamera(BottoneSwitch btn)
{
if (!btn.isON){// mostro la post
Debug.Write("mostra fotocamera post");
}else{//mostra ant
Debug.Write("mostra fotocamera ant");
}
}
}
}
so using this event:
public void gestisciFlash(BottoneSwitch btn){
if (flash_event != null)
flash_event.Invoke(btn, null);
}
I should be able to print "teeest" but flash_event is always null and I don't know what to do.
I've tried the solution presented here firing custom render event but with no luck.
Any suggestions?
EDIT
protected override void OnElementChanged(ElementChangedEventArgs<CameraStream> e)
{
if(e.OldElement != null){
}
if(e.NewElement != null){
if (Control == null)
{
uiCameraPreview = new UICameraPreview(new Camera.CameraOptions());
SetNativeControl(uiCameraPreview);
}
var camera_stream = (CameraStream)e.NewElement;
camera_stream.flash_event += (object sender, EventArgs ea) => {
attivaFlash();
};
}
base.OnElementChanged(e);
}
refactored the code as suggested by #Benl still not working , this:
public void gestisciFlash(BottoneSwitch btn){
if (flash_event != null)
flash_event.Invoke(btn, null);
}
Is still null.
Edit:
Assumption: Some camera device generating the flash/torch light event is attached.
CameraStreamRenderer.cs:
OnElementChanged(ElementChangedEventArgs<CameraStream> e)
{
base.OnElementChanged(e);
if (Control == null)
{
// Instantiate the native control and assign it to the Control property with
// the SetNativeControl method
uiCameraPreview = new UICameraPreview(e.NewElement.Camera);
SetNativeControl(uiCameraPreview);
}
if (e.OldElement != null)
{
// Unsubscribe from event handlers and cleanup any resources
uiCameraPreview.Flash -= OnFlash;
}
if (e.NewElement != null)
{
// Configure the control and subscribe to event handlers
uiCameraPreview.Flash += OnFlash;
}
}
void OnFlash(object sender, EventArgs e)
{
Debug.WriteLine("Flash");
}
However, the flash event must be generated somewhere, not shown in the original question.
Assuming the flash event might be generated in UICameraPreview in an Observer with assumed key "torchActive".
UICameraPreview.cs:
public event EventHandler<EventArgs> Flash;
protected virtual void OnFlash()
{
Flash?.Invoke(this, new EventArgs());
}
void Initialize()
{
...
AVCaptureDevice device = ...
...
if (device == null)
{
return;
}
device.AddObserver(
key: "torchActive",
options: NSKeyValueObservingOptions.New,
observer: (c) =>
{
OnFlash();
});
device.RemoveObserver not shown above.

Custom renderer for Tabbed page in Android: Xamarin Forms

I am working on a Xamarin.Forms application. I have this tabbed page renderer in iOS:
public class TabbedPageRenderer : TabbedRenderer
{
private MainPage _page;
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
_page = (MainPage)e.NewElement;
}
else
{
_page = (MainPage)e.OldElement;
}
try
{
var tabbarController = (UITabBarController)this.ViewController;
if (null != tabbarController)
{
tabbarController.ViewControllerSelected += OnTabBarReselected;
}
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
}
private void OnTabBarReselected(object sender, UITabBarSelectionEventArgs e)
{
var tabs = Element as TabbedPage;
var playTab = tabs.Children[4];
if (TabBar.SelectedItem.Title == "Play")
{
if (tabs != null)
{
playTab.Title = "Pause";
playTab.Icon = "pause.png";
}
App.pauseCard = false;
}
else
{
if (tabs != null)
{
playTab.Title = "Play";
playTab.Icon = "play.png";
}
App.pauseCard = true;
}
}
}
This basically changes the icon on my Play tab to pause and play. This is working good in iOS. But I am struggling on how to have the same function (basically convert this to Android) in Android side.
Can any point me to the right direction? Basically help me? :-)
Note: I am pretty new with Android development.
EDIT: This is what it would look like in iOS.
Pause Mode:
Play Mode:
There is a blog post by Xamarin's James Montemagno which explains how to achieve this requirement.
Basically, it uses Custom Tab which inherits from TabbedPage which initialize an event UpdateIcons to be fired on Tab Current Page Changed event CurrentPageChanged
public class MyTabs : TabbedPage
{
//always save a reference to the current page
Page currentPage;
public MyTabs()
{
//create the pages and set the view models
//you could also do this in the page code behind
Children.Add(new TabIconsPage
{
BindingContext = new Tab1ViewModel
{
IsSelected = true
}
});
Children.Add(new TabIconsPage2
{
BindingContext = new Tab2ViewModel()
});
currentPage = Children[0];
//Register for page changes
this.CurrentPageChanged += Handle_CurrentPageChanged;
}
//Update the IsSelected state and trigger an Event that anyone can loop into.
public event EventHandler UpdateIcons;
void Handle_CurrentPageChanged(object sender, EventArgs e)
{
var currentBinding = currentPage.BindingContext as IIconChange;
if (currentBinding != null)
currentBinding.IsSelected = false;
currentPage = CurrentPage;
currentBinding = currentPage.BindingContext as IIconChange;
if (currentBinding != null)
currentBinding.IsSelected = true;
UpdateIcons?.Invoke(this, EventArgs.Empty);
}
}
Now Android needs a custom renderer to subscribe to the UpdateIcons event and perform icon changes
public class MyTabs : TabbedPage
{
//always save a reference to the current page
Page currentPage;
public MyTabs()
{
//create the pages and set the view models
//you could also do this in the page code behind
Children.Add(new TabIconsPage
{
BindingContext = new Tab1ViewModel
{
IsSelected = true
}
});
Children.Add(new TabIconsPage2
{
BindingContext = new Tab2ViewModel()
});
currentPage = Children[0];
//Register for page changes
this.CurrentPageChanged += Handle_CurrentPageChanged;
}
//Update the IsSelected state and trigger an Event that anyone can loop into.
public event EventHandler UpdateIcons;
void Handle_CurrentPageChanged(object sender, EventArgs e)
{
var currentBinding = currentPage.BindingContext as IIconChange;
if (currentBinding != null)
currentBinding.IsSelected = false;
currentPage = CurrentPage;
currentBinding = currentPage.BindingContext as IIconChange;
if (currentBinding != null)
currentBinding.IsSelected = true;
UpdateIcons?.Invoke(this, EventArgs.Empty);
}
}

Setting custom marker image would overlay default marker icon along with custom icon

After updating version of forms to 2.4.0.282, I started getting this weird behavior in MapView. I have created a custom renderer for map in android where I am setting marker images as per my requirement. Custom markers in fact appear, but on top of it, it's default icon still gets overlaid.
Note that I am using Xamarin.Maps version 2.4.0.282, tried to downgrade to prior versions as well but I am of no help.
I even tried it out by commenting line,
Forms.SetFlags("FastRenderers_Experimental");
but even this didn't help.
Below is the renderer I created,
public class CustomMapRenderer : MapRenderer, IOnMapReadyCallback
{
GoogleMap map;
public static double PreviousDistance = 0;
List<CustomPin> customPins;
CustomMap formsMap = null;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
map.InfoWindowClick -= OnInfoWindowClick;
map.MarkerClick -= OnMarkerClick;
}
if (e.NewElement != null)
{
formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
((MapView)Control).GetMapAsync(this);
}
}
protected override void OnMapReady(GoogleMap googleMap)
{
map = googleMap;
map.InfoWindowClick += OnInfoWindowClick;
map.MarkerClick += OnMarkerClick;
map.UiSettings.ZoomControlsEnabled = false;
formsMap.MoveToRegion(MapSpan.FromCenterAndRadius(formsMap.Location, Distance.FromMiles(1.0)));
if(customPins != null && customPins.Count > 0)
{
setMapPins("CustomPins");
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == "CustomPins" || (e.PropertyName.Equals("VisibleRegion")))
{
setMapPins(e.PropertyName);
}
}
private void setMapPins(string PropertyName)
{
customPins = formsMap.CustomPins;
map.Clear();
if (customPins != null && customPins.Count > 0)
{
if (PropertyName == "CustomPins")
{
//Set map zoom
var defaultZoom = 14;
try
{
PreviousDistance = DistanceCalculation.MoveToRegionData.MoveToRegion(formsMap, customPins, defaultZoom, PreviousDistance);
}
catch (Exception ex)
{
PreviousDistance = 0;
Console.WriteLine(ex.Message);
}
}
foreach (var pin in customPins)
{
var pinImage = Resources.GetIdentifier(pin.PinImage.ToLower(), "drawable", Context.PackageName);
var markerImg = BitmapDescriptorFactory.FromResource(pinImage);
map.AddMarker(new MarkerOptions().SetTitle(pin.Pin.Label).SetSnippet(pin.Id).SetPosition(new LatLng(pin.Pin.Position.Latitude, pin.Pin.Position.Longitude)).SetIcon(markerImg));
}
}
else
{
Console.WriteLine("In else!!");
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
if (changed)
{
}
}
void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin(e.Marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (!string.IsNullOrWhiteSpace(customPin.Url))
{
var url = global::Android.Net.Uri.Parse(customPin.Url);
var intent = new Intent(Intent.ActionView, url);
intent.AddFlags(ActivityFlags.NewTask);
global::Android.App.Application.Context.StartActivity(intent);
}
}
CustomPin GetCustomPin(Marker annotation)
{
var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
foreach (var pin in customPins)
{
if (pin.Pin.Position == position)
{
return pin;
}
}
return null;
}
void OnMarkerClick(object sender, GoogleMap.MarkerClickEventArgs ea)
{
var marker = (Marker) ea.Marker;
formsMap.IsPinClicked = false;
var customPin = GetCustomPin(marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
formsMap.SelectedPinId = Convert.ToInt32(marker.Snippet);
formsMap.IsPinClicked = true;
}
}
This is what it looks like right now..
I have created a custom renderer for map in android where I am setting marker images as per my requirement. Custom markers in fact appear, but on top of it, it's default icon still gets overlaid.
I've made a demo and reproduced the problem, after some tests I found that the overriding of OnMapReady function causes the problem. Even with totally empty OnMapReady the problem will occurs. My guess is a call to the OnMapReady in Custom Map Render might lead to a rerender of the Map including pins.
Solution:
Comment OnMapReady out.
Move the logic in OnMapReady to OnElementChanged.
Let your local variable map=NativeMap and make sure setMapPins get called in OnElementPropertyChanged.
Add NativeMap.Clear(); in OnElementPropertyChanged and try again. Hope it will help!!!
Have a look below code for customizing pin on Android Platform.
[assembly:ExportRenderer (typeof(CustomMap), typeof(CustomMapRenderer))]
namespace CustomRenderer.Droid
{
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter
{
List<CustomPin> customPins;
bool isDrawn;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
NativeMap.InfoWindowClick -= OnInfoWindowClick;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
((MapView)Control).GetMapAsync(this);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName.Equals("VisibleRegion") && !isDrawn)
{
NativeMap.Clear();
NativeMap.InfoWindowClick += OnInfoWindowClick;
NativeMap.SetInfoWindowAdapter(this);
foreach (var pin in customPins)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Pin.Position.Latitude, pin.Pin.Position.Longitude));
marker.SetTitle(pin.Pin.Label);
marker.SetSnippet(pin.Pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
NativeMap.AddMarker(marker);
}
isDrawn = true;
}
}
}
}
For more detail Click here

Floating Action Button in Xamarin.Forms

I had completed my App's home page in Xamarin.Forms Portable.
Now i want to add a Flotation Action Button In my Android Project !
Is there any way to add FAB for Android in my existing home page, which was coded in Xamarin.Forms Portable.
OR
I want to create a separate home page for Android and add call it as a MainPage for android ?
Thanks and Regards.
Before the official support library came out I ported the FAB over.
There is now a Xamarin.Forms sample in my GitHub repo that you can use: https://github.com/jamesmontemagno/FloatingActionButton-for-Xamarin.Android
Build a Custom Control
For the FAB's properties to be bindable in Xamarin.Forms, we need a custom control with bindable properties.
public class FloatingActionButtonView : View
{
public static readonly BindableProperty ImageNameProperty = BindableProperty.Create<FloatingActionButtonView,string>( p => p.ImageName, string.Empty);
public string ImageName
{
get { return (string)GetValue (ImageNameProperty); }
set { SetValue (ImageNameProperty, value); }
}
public static readonly BindableProperty ColorNormalProperty = BindableProperty.Create<FloatingActionButtonView,Color>( p => p.ColorNormal, Color.White);
public Color ColorNormal
{
get { return (Color)GetValue (ColorNormalProperty); }
set { SetValue (ColorNormalProperty, value); }
}
public static readonly BindableProperty ColorPressedProperty = BindableProperty.Create<FloatingActionButtonView,Color>( p => p.ColorPressed, Color.White);
public Color ColorPressed
{
get { return (Color)GetValue (ColorPressedProperty); }
set { SetValue (ColorPressedProperty, value); }
}
public static readonly BindableProperty ColorRippleProperty = BindableProperty.Create<FloatingActionButtonView,Color>( p => p.ColorRipple, Color.White);
public Color ColorRipple
{
get { return (Color)GetValue (ColorRippleProperty); }
set { SetValue (ColorRippleProperty, value); }
}
...
}
We will then map each property to a corresponding property on the native FAB control.
Attach a Renderer
If we want to use a native control in Xamarin.Forms, we need a renderer. For simplicity, lets use a ViewRenderer. This renderer will map our custom FloatingActionButtonView to an Android.Widget.FrameLayout.
public class FloatingActionButtonViewRenderer : ViewRenderer<FloatingActionButtonView, FrameLayout>
{
...
private readonly Android.Content.Context context;
private readonly FloatingActionButton fab;
public FloatingActionButtonViewRenderer()
{
context = Xamarin.Forms.Forms.Context;
fab = new FloatingActionButton(context);
...
}
protected override void OnElementChanged(ElementChangedEventArgs<FloatingActionButtonView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || this.Element == null)
return;
if (e.OldElement != null)
e.OldElement.PropertyChanged -= HandlePropertyChanged;
if (this.Element != null) {
//UpdateContent ();
this.Element.PropertyChanged += HandlePropertyChanged;
}
Element.Show = Show;
Element.Hide = Hide;
SetFabImage(Element.ImageName);
fab.ColorNormal = Element.ColorNormal.ToAndroid();
fab.ColorPressed = Element.ColorPressed.ToAndroid();
fab.ColorRipple = Element.ColorRipple.ToAndroid();
var frame = new FrameLayout(Forms.Context);
frame.RemoveAllViews();
frame.AddView(fab);
SetNativeControl (frame);
}
public void Show(bool animate = true)
{
fab.Show(animate);
}
public void Hide(bool animate = true)
{
fab.Hide(animate);
}
void HandlePropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Content") {
Tracker.UpdateLayout ();
}
else if (e.PropertyName == FloatingActionButtonView.ColorNormalProperty.PropertyName)
{
fab.ColorNormal = Element.ColorNormal.ToAndroid();
}
else if (e.PropertyName == FloatingActionButtonView.ColorPressedProperty.PropertyName)
{
fab.ColorPressed = Element.ColorPressed.ToAndroid();
}
else if (e.PropertyName == FloatingActionButtonView.ColorRippleProperty.PropertyName)
{
fab.ColorRipple = Element.ColorRipple.ToAndroid();
}
...
}
void SetFabImage(string imageName)
{
if(!string.IsNullOrWhiteSpace(imageName))
{
try
{
var drawableNameWithoutExtension = Path.GetFileNameWithoutExtension(imageName);
var resources = context.Resources;
var imageResourceName = resources.GetIdentifier(drawableNameWithoutExtension, "drawable", context.PackageName);
fab.SetImageBitmap(Android.Graphics.BitmapFactory.DecodeResource(context.Resources, imageResourceName));
}
catch(Exception ex)
{
throw new FileNotFoundException("There was no Android Drawable by that name.", ex);
}
}
}
}
Pull it all Together
OK! We've built the custom control, and mapped it to a renderer. The last step is laying out the control in our view.
public class MainPage : ContentPage
{
public MainPage()
{
var fab = new FloatingActionButtonView() {
ImageName = "ic_add.png",
ColorNormal = Color.FromHex("ff3498db"),
ColorPressed = Color.Black,
ColorRipple = Color.FromHex("ff3498db")
};
// Main page layout
var pageLayout = new StackLayout {
Children =
{
new Label {
VerticalOptions = LayoutOptions.CenterAndExpand,
XAlign = TextAlignment.Center,
Text = "Welcome to Xamarin Forms!"
}
}};
var absolute = new AbsoluteLayout() {
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand };
// Position the pageLayout to fill the entire screen.
// Manage positioning of child elements on the page by editing the pageLayout.
AbsoluteLayout.SetLayoutFlags(pageLayout, AbsoluteLayoutFlags.All);
AbsoluteLayout.SetLayoutBounds(pageLayout, new Rectangle(0f, 0f, 1f, 1f));
absolute.Children.Add(pageLayout);
// Overlay the FAB in the bottom-right corner
AbsoluteLayout.SetLayoutFlags(fab, AbsoluteLayoutFlags.PositionProportional);
AbsoluteLayout.SetLayoutBounds(fab, new Rectangle(1f, 1f, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
absolute.Children.Add(fab);
Content = absolute;
}
}
Complete code on Github : Floating Action Button Xamarin.Forms

Show 3dmodel after tracking lost metaio sdk

I am using metaio sdk 6.0.2. i am working on metaio INSTANT_2D_GRAVITY tracking and was able to display 3d model. I want to display same 3d model when tracking is lost.but I am failing to do so. I tried by adding trackingValuesVector in onTrackingEvent of MetaioSDKCallbackHandler with no success. can anyone tell me where am I going wrong?
private TrackingValues mTrackingValues;// declared globally
private IGeometry mModel; // declared globally
private boolean mPreview=true;// declared globally
// start INSTANT_2D_GRAVITY tracking
public void onTakePicture(View v)
{
captureTrackingValues = true;
metaioSDK.startInstantTracking("INSTANT_2D_GRAVITY", new File(""), mPreview);
mPreview = !mPreview;
}
final class MetaioSDKCallbackHandler extends IMetaioSDKCallback
{
#Override
public void onInstantTrackingEvent(final boolean success,final File filePath) {
super.onInstantTrackingEvent(success, filePath);
if(mSurfaceView != null)
{
mSurfaceView.queueEvent(new Runnable() {
#Override
public void run() {
if(success)
{
if(captureTrackingValues == true)
{
metaioSDK.setTrackingConfiguration(filePath);
Log.i("Tracking value success","good");
}
}
else
{
Log.i("Tracking value failure","bad");
}
}
});
}
}
#Override
public void onTrackingEvent(TrackingValuesVector trackingValuesVector) {
super.onTrackingEvent(trackingValuesVector);
if (!trackingValuesVector.isEmpty())
{
for(int i =0;i< trackingValuesVector.size();i++)
{
if(trackingValuesVector.get(i).isTrackingState() && mModel!=null)
{
mTrackingValues = metaioSDK.getTrackingValues(i);
mModel.setCoordinateSystemID(trackingValuesVector.get(i).getCoordinateSystemID());
}
else {
if(mModel!= null && mTrackingValues != null) {
metaioSDK.setCosOffset(1, mTrackingValues);
//mChairModel.setCoordinateSystemID(0);
Log.e("TestAR","isTrackingState is null");
}
}
}
}
else{
if(mModel!= null && mTrackingValues != null) {
metaioSDK.setCosOffset(1, mTrackingValues);
//mModel.setCoordinateSystemID(0);
Log.e("TestAR","trackingValuesVector is null");
}
}
}
}
loading 3d model:
private void loadModel()
{
if (mSurfaceView != null) {
mSurfaceView.queueEvent(new Runnable() {
#Override
public void run() {
File chairModel = AssetsManager.getAssetPathAsFile(getApplicationContext(),"chair.obj");
if (chairModel != null) {
mModel = metaioSDK.createGeometry(chairModel);
mModel.setScale(3f);
mModel.setTranslation(new Vector3d(0f,0f,-60f));
mGestureHandler.addObject(mModel, 1);
mModel.setRotation(new Rotation(0f, 0.5f, 0f));
mModel.setCoordinateSystemID(1);
}
}
});
}
else
{
Log.e("exception", "msurfaceview is null");
}
}
I see that you also tried setting the model to COS 0. This should actually work, if the tracking is lost.
If you do not see the model, you would have to play around with the scale value (i.e. set a low value like 0.01) and with the Z translation value. Set a negative Z value in order to move the model away from the camera clipping plane.

Categories

Resources