Xamarin android custom view with arcore - xamarin.forms

I looked at this video to make a simple AR with Arcore for Android:
Augmented Reality with Arcore
Everything went well, but now I want to see this in xamarin forms, in a custom view.
But I can not :(
what i tried last:
In Forms:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:FormsAR.Controls"
x:Class="FormsAR.MainPage">
<StackLayout BackgroundColor="Red" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Label BackgroundColor="Blue" TextColor="White" Text="JUST TEST"/>
<controls:ARView HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BackgroundColor="Yellow"/>
</StackLayout>
</ContentPage>
And:
using System;
using Xamarin.Forms;
namespace FormsAR.Controls
{
public class ARView : View
{
}
}
And Android:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/fragmentAR"
android:name="com.google.ar.sceneform.ux.ArFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
and finally:
[assembly: ExportRenderer(typeof(ARView), typeof(ARViewRenderer))]
namespace FormsAR.Droid.Controls
{
public class ARViewRenderer : ViewRenderer<ARView, Android.Views.View>
{
private ArFragment arFragment;
private Material readyColor;
Android.Views.View _view;
public ARViewRenderer(Context context) : base(context){}
protected override void OnElementChanged(ElementChangedEventArgs<ARView> e)
{
base.OnElementChanged(e);
_view = LayoutInflater.From(this.Context).Inflate(Resource.Layout.ARLayout, null, false);
AddView(_view);
if (Control == null)
{
Initialize();
}
}
private void Initialize()
{
var activity = this.Context as AppCompatActivity;
arFragment = (ArFragment)activity.SupportFragmentManager.FindFragmentById(Resource.Id.fragmentAR);
setupPlane();
MaterialFactory.MakeOpaqueWithColor(Context, new Google.AR.Sceneform.Rendering.Color(Android.Graphics.Color.Yellow)).GetAsync().ContinueWith(materialTask =>
{
readyColor = (Material)materialTask.Result;
});
}
private void setupPlane()
{
arFragment.TapArPlane += (sender, args) => this.OnTapArPlaneListener(args.HitResult, args.Plane, args.MotionEvent);
}
private void OnTapArPlaneListener(HitResult hitResult, Plane plane, MotionEvent motionEvent)
{
Anchor anchor = hitResult.CreateAnchor();
AnchorNode anchorNode = new AnchorNode(anchor);
anchorNode.SetParent(arFragment.ArSceneView.Scene);
CreateModel(anchorNode);
}
private void CreateModel(AnchorNode anchorNode)
{
TransformableNode node = new TransformableNode(arFragment.TransformationSystem);
node.SetParent(anchorNode);
var nodeRenderable = ShapeFactory.MakeSphere(0.08f, new Google.AR.Sceneform.Math.Vector3(0.0f, 0.15f, 0.0f), readyColor);
node.Renderable = nodeRenderable;
node.Select();
}
}
}
but i can't see:
image result
Link for project
Can you please help me identify where error?

Related

Weird space on top of the listview after updating iOS to 15

I'm using
Xamarin.Froms: 5.0.0.24125
Xcode Version: 13.0 (13A233)
Visual Studio for Mac Community Version 8.10.9 (Build 3)
NuGet Version: 5.9.0.7134
Xamarin.iOS Version: 14.20.0.27 (Visual Studio Community)
MainPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ListViewTopSpace.MainPage"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
Title="Page title"
ios:Page.UseSafeArea="true">
<ListView ItemsSource="{Binding Users}"
BackgroundColor="Blue"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Margin="0,0,0,0.5"
Padding="10,0"
BackgroundColor="AliceBlue">
<Label FontSize="12">
<Label.FormattedText>
<FormattedString>
<Span Text="User Id: "
FontAttributes="Bold"/>
<Span Text="{Binding Id, Mode=OneWay}"/>
</FormattedString>
</Label.FormattedText>
</Label>
<Label FontSize="12">
<Label.FormattedText>
<FormattedString>
<Span Text="User name: "
FontAttributes="Bold"/>
<Span Text="{Binding Name, Mode=OneWay}"/>
</FormattedString>
</Label.FormattedText>
</Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
MainPageViewModel.cs
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace ListViewTopSpace
{
public class MainPageViewModel : ObservableProperty
{
public MainPageViewModel()
{
Users = new ObservableCollection<UserListModel>()
{
new UserListModel { Id = 1, Name = "User 1" },
new UserListModel { Id = 2, Name = "Test user 1" },
new UserListModel { Id = 3, Name = "User 2" },
new UserListModel { Id = 4, Name = "Test user 2" },
};
}
private ObservableCollection<UserListModel> _users;
public ObservableCollection<UserListModel> Users
{
get { return _users; }
set
{
_users = value;
OnPropertyChanged(nameof(Users));
}
}
}
public class UserListModel
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ObservableProperty : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Result:
[1]: https://i.stack.imgur.com/BKn29.png
This highlighted space shouldn’t be there.
#Colex is right, but you have to replace this string of code:
Control.TableHeaderView = new UIKit.UIView(frame: new CoreGraphics.CGRect(0, 0, 0, float.MinValue));
with this:
Control.SectionHeaderTopPadding = new nfloat(0);
Full code of Renderer:
[assembly: ExportRenderer(typeof(ListView), typeof(LVRenderer))]
namespace YourNameSpace.iOS
{
public class LVRenderer : ListViewRenderer
{
public LVRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.SectionHeaderTopPadding = new nfloat(0);
}
}
}
}
The problem is related with apple not xamarin side .
Please see this thread : https://developer.apple.com/forums/thread/683980.
To solve it we can add a custom renderer for ListView
Try the following code
[assembly:ExportRenderer(typeof(ListView),typeof(MyRenderer))]
namespace Formssss.iOS
{
public class MyRenderer : ListViewRenderer
{
public MyRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if(Control != null)
{
Control.TableHeaderView = new UIKit.UIView(frame: new CoreGraphics.CGRect(0, 0, 0, float.MinValue));
}
}
}
}
After updating to xamarin.forms version to latest from NuGet manager will solve this issue.
Updating to version 5.0.0.2244 will definitely fix this issue
and extra top padding from list view will be removed on iOS 15.0 or greater

How to add a icon in Picker dropdown popup list using xamarin forms

We are using picker control our app. I want to customize the picker while clicking the dropdown popup list will be shown. In that popup, I want to display the left side icon with the text. How could be achieve using xamarin forms picker android and ios platform?
You could use CustomRenderer to achieve this.
Here is a sample:
Create a custom picker:
public class MyPicker : Picker
{
}
For Android:
Create a custom renderer in Android:
public class CustomPicker : PickerRenderer
{
private Dialog dialog;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
Control.Click += Control_Click1;
}
protected override void Dispose(bool disposing)
{
Control.Click -= Control_Click1;
base.Dispose(disposing);
}
private void Control_Click1(object sender, EventArgs e)
{
//throw new NotImplementedException();
Picker model = Element;
dialog = new Dialog(Forms.Context);
dialog.SetContentView(Resource.Layout.custom_picker_dialog);
Android.Widget.ListView listView = (Android.Widget.ListView)dialog.FindViewById(Resource.Id.listview);
//listView.Adapter = new CustomPickerAdapter(((List<PickerModel>)model.ItemsSource), model.SelectedIndex);
listView.Adapter = new MyAdaptr((List<string>)model.ItemsSource);
listView.ItemClick += (object sender1, ItemClickEventArgs e1) =>
{
Element.SelectedIndex = e1.Position;
dialog.Hide();
};
if (model.ItemsSource.Count > 3)
{
var height = Xamarin.Forms.Application.Current.MainPage.Height;
var width = Xamarin.Forms.Application.Current.MainPage.Width;
dialog.Window.SetLayout(700, 800);
}
dialog.Show();
}
class MyAdaptr : BaseAdapter
{
private IList<string> mList;
public MyAdaptr(IList<string> itemsSource)
{
mList = itemsSource;
}
public override int Count => mList.Count;
public override Java.Lang.Object GetItem(int position)
{
return mList[position];
}
public override long GetItemId(int position)
{
return position;
}
public override Android.Views.View GetView(int position, Android.Views.View convertView, ViewGroup parent)
{
Android.Views.View view = convertView;
convertView = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.celllayout, null); //here is the style you want to achieve
TextView text = convertView.FindViewById<TextView>(Resource.Id.textview1);
text.Text = mList[position];
return convertView;
}
}
}
custom_picker_dialog.axml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<TextView
android:text="Select One Option"
android:layout_width="200dp"
android:layout_height="25dp"
android:paddingLeft="25dp"
android:paddingRight="25dp"/>
<ListView
android:id="#+id/listview"
android:layout_gravity="center"
android:background="#drawable/abc_list_pressed_holo_light"
android:layout_width="match_parent"
android:dividerHeight="3dp"
android:layout_height="0dp"
android:layout_weight="1">
</ListView>
</LinearLayout>
celllayout.axml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<ImageView
android:id="#+id/img"
android:src="xxx"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="#+id/textview1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
/>
</LinearLayout>
For ios:
Create a custom renderer in ios
public class CustomPicker : PickerRenderer
{
List<string> itemList;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
Picker myPicker = Element;
itemList = myPicker.Items.ToList();
UITextField textField = Control;
UIPickerView pickerView = textField.InputView as UIPickerView;
pickerView.Delegate = new MyPickerViewDelegate(itemList,Control);
textField.InputView = pickerView;
var toolbar = new UIToolbar(new CoreGraphics.CGRect(0, 0, UIScreen.MainScreen.Bounds.Size.Width , 1)) { BarStyle = UIBarStyle.Default, Translucent = true };
textField.InputAccessoryView = toolbar;
}
}
internal class MyPickerViewDelegate : UIPickerViewDelegate
{
private List<string> itemList;
private UITextField textField;
public MyPickerViewDelegate(List<string> itemList, UITextField control)
{
this.itemList = itemList;
this.textField = control;
}
//Define the Font size or style
public override NSAttributedString GetAttributedTitle(UIPickerView pickerView, nint row, nint component)
{
var text = new NSAttributedString(
itemList[(int)row],
font: UIFont.SystemFontOfSize(24),
foregroundColor: UIColor.Red,
strokeWidth: 4
);
return text;
}
//Define the row height
public override nfloat GetRowHeight(UIPickerView pickerView, nint component)
{
return 45;
}
//define the itemview in your listview,you could add a UIImage here for your scene
public override UIView GetView(UIPickerView pickerView, nint row, nint component, UIView view)
{
UIView contentView = new UIView(new CoreGraphics.CGRect(0, 0, UIScreen.MainScreen.Bounds.Size.Width, 45));
UILabel label = new UILabel();
label.Frame = contentView.Bounds;
contentView.AddSubview(label);
label.Text = itemList[(int)row];
return contentView;
}
public override void Selected(UIPickerView pickerView, nint row, nint component)
{
//base.Selected(pickerView, row, component);
textField.Text = itemList[(int)row];
textField.ResignFirstResponder();
}
}
Finally you could use in your page.xaml like:
<local:MyPicker x:Name="picker" Title="Select An option" />

Color in custom MaterialEntryRenderer

Why this line
Control.BackgroundTintList = ColorStateList.ValueOf(Android.Graphics.Color.Green);
is working in a custom EntryRenderer in Android (xamarin forms) and not in a custom MaterialEntryRenderer?
I want to change only the underline color of the entry in material and not the underline color and the placeholder text color too.
xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Genesys.App.Views.ConfigurationView"
xmlns:local1="clr-namespace:Genesys.App.CustomControls"
Title="Ingrese Configuración"
Visual="Material">
<ContentPage.Content>
<StackLayout VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Padding="15"
Background="white">
<local1:MyEntry x:Name="txtServicio"
Placeholder="SERVICIO"
BackgroundColor="White">
</local1:MyEntry>
<Button Padding="5"
Margin="5"
Text="guardar"
TextTransform="Uppercase"
TextColor="white"
BackgroundColor="{StaticResource colorPrimary}"
HorizontalOptions="End"
Command="{Binding ButtonCommand}"></Button>
</StackLayout>
</ContentPage.Content>
</ContentPage>
//class MyEntry
public class MyEntry : Entry
{
}
CustomEntryRenderer in android
[assembly: ExportRenderer(typeof(MyEntry), typeof(MyRenderer))]
namespace Genesys.App.Droid
{
public class MyRenderer : EntryRenderer//this works
{
public MyRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
this.EditText.FocusChange += (sender, ee) => {
bool hasFocus = ee.HasFocus;
if (hasFocus)
{
Control.BackgroundTintList = ColorStateList.ValueOf(Android.Graphics.Color.Green); //changes color
}
else
{
Control.BackgroundTintList = ColorStateList.ValueOf(Android.Graphics.Color.Red); //changes color
}
};
}
}
}
}
//don't work
[assembly: ExportRenderer(typeof(MyEntry), typeof(MyRenderer))]
namespace Genesys.App.Droid
{
public class MyRenderer : MaterialEntryRenderer//don't work
{
public MyRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
this.EditText.FocusChange += (sender, ee) => {
bool hasFocus = ee.HasFocus;
if (hasFocus)
{
Control.BackgroundTintList = ColorStateList.ValueOf(Android.Graphics.Color.Green);//doesn't change color
}
else
{
Control.BackgroundTintList = ColorStateList.ValueOf(Android.Graphics.Color.Red); //doesn't change color
}
};
}
}
}
}

Steps to create a behavior (how create a behavior)

i need to create a behavior from a view, i am using a class, also namespace to call to behavior but it's not working for me, i do not know what is my wrong, because i am doing all of step to create a behavior.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Layouts.Commands.BasketView"
xmlns:local="clr-namespace:Layouts;assembly=Layouts"
Title="Cart">
<StackLayout Padding="10,60,10,0">
<Label Text="Red when the number isn't valid" FontSize="Small" />
<Entry Placeholder="Enter a System.Double"
local:NumericValidationBehavior.AttachBehavior="true" />
</StackLayout>
</ContentPage>
in this line i get the error local:NumericValidationBehavior.AttachBehavior="true" />
i have my behavior, but it cannot be instanciate in the view (see code up), there are other step to be instanciate?
namespace AttachedNumericValidationBehavior
{
public static class NumericValidationBehavior
{
public static readonly BindableProperty AttachBehaviorProperty
= BindableProperty.CreateAttached("AttachBehavior", typeof(bool), typeof(NumericValidationBehavior), false, propertyChanged: OnAttachBehaviorChanged);
public static bool GetAttachBehavior(BindableObject view)
{
return (bool)view.GetValue(AttachBehaviorProperty);
}
public static void SetAttachBehavior(BindableObject view, bool value)
{
view.SetValue(AttachBehaviorProperty, value);
}
static void OnAttachBehaviorChanged(BindableObject view, object oldValue, object newValue)
{
var entry = view as Entry;
if (entry == null)
{
return;
}
bool attachBehavior = (bool)newValue;
if (attachBehavior)
{
entry.TextChanged += OnEntryTextChanged;
}
else
{
entry.TextChanged -= OnEntryTextChanged;
}
}
static void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
double result;
bool isValid = double.TryParse(args.NewTextValue, out result);
((Entry)sender).TextColor = isValid ? Color.Default : Color.Red;
}
}
}
the error is: "AttachBehavior is not in type NumericValidationBehavior"
You class NumericValidationBehavior belongs to namespace AttachedNumericValidationBehavior, so when you want to use NumericValidationBehavior in xaml, the namespace local should be:
xmlns:local="clr-namespace:AttachedNumericValidationBehavior;"
And use the namespace:
<StackLayout Padding="10,60,10,0">
<Label Text="Red when the number isn't valid" FontSize="Small" />
<Entry Placeholder="Enter a System.Double"
local:NumericValidationBehavior.AttachBehavior="true" />
</StackLayout>
For more details, see document: Declaring Namespaces for Types

Xamarin Forms how to add behaviors to custom control

I have created a custom control,which is a ContentView with a Label and an Entry
The xaml of the custom controls looks like this:
<Label Text="{Binding Source={x:Reference ValidationControl}, Path=Caption}"/>
<Entry Text="{Binding Source={x:Reference ValidationControl}, Path=Value, Mode=TwoWay}" />
The code behind of the custom control looks like this:
public static readonly BindableProperty CaptionProperty = BindableProperty.Create(
nameof(Caption), typeof(string), typeof(ValidationEntry), default(string));
public string Caption
{
get => (string)GetValue(CaptionProperty);
set => SetValue(CaptionProperty, value);
}
public static readonly BindableProperty ValueProperty = BindableProperty.Create(
nameof(Value), typeof(string), typeof(ValidationEntry), default(string));
public string Value
{
get => (string)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
I’m using the custom control in the following way
<controls:ValidationEntry Caption=”Name:” Value="{Binding FullName, Mode=TwoWay}" />
My question is how to add behaviors to the custom control?
I would like to add them in the place that I’m using the control. i.e.
<controls:ValidationEntry Caption="Name:"
Value="{Binding FullName, Mode=TwoWay}">
<controls:ValidationEntry.EntryBehaviors>
<behaviors:EntryLengthValidatorBehavior IgnoreSpaces="True"/>
</controls:ValidationEntry.EntryBehaviors>
</controls:ValidationEntry>
You can create a behaviors directly, I add a NumericValidationBehavior in my custom entry to check the data if it is double.If type of the data is not double, the color of text will be set to red.
Here is xaml code.
<StackLayout>
<local:MyEntry local:NumericValidationBehavior.AttachBehavior="true">
</local:MyEntry>
</StackLayout>
Here is NumericValidationBehavior.cs
public static class NumericValidationBehavior
{
public static readonly BindableProperty AttachBehaviorProperty =
BindableProperty.CreateAttached(
"AttachBehavior",
typeof(bool),
typeof(NumericValidationBehavior),
false,
propertyChanged: OnAttachBehaviorChanged);
public static bool GetAttachBehavior(BindableObject view)
{
return (bool)view.GetValue(AttachBehaviorProperty);
}
public static void SetAttachBehavior(BindableObject view, bool value)
{
view.SetValue(AttachBehaviorProperty, value);
}
static void OnAttachBehaviorChanged(BindableObject view, object oldValue, object newValue)
{
var entry = view as Entry;
if (entry == null)
{
return;
}
bool attachBehavior = (bool)newValue;
if (attachBehavior)
{
entry.TextChanged += OnEntryTextChanged;
}
else
{
entry.TextChanged -= OnEntryTextChanged;
}
}
static void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
double result;
bool isValid = double.TryParse(args.NewTextValue, out result);
((Entry)sender).TextColor = isValid ? Color.Default : Color.Red;
}
}
Update
I create a custom view with ContentView
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BeHavDemo.MyView">
<ContentView.Content>
<StackLayout>
<Label Text="xxxx"/>
<Entry Text="eeeee" />
</StackLayout>
</ContentView.Content>
</ContentView>
Then I create a behavior.
public class MyBeha : Behavior<MyView>
{
protected override void OnAttachedTo(BindableObject view)
{
base.OnAttachedTo(view);
var myview=view as MyView;
StackLayout stackLayout = (StackLayout)myview.Content;
Label label = (Label)stackLayout.Children[0];
Entry entry=(Entry) stackLayout.Children[1];
}
}

Resources