Hi I created the application using Xamarin.Forms - Its working fine in iOS but Certainly Its not working in Android and facing some issues mentioned in the title.
Lemme explain my application and code continues, The application having one login screen and will move on to home screen called HomeController using navigation in this just populating list view and its cell. While tapping each row will move on to Master detail page in this I'm getting error and it's working fine in iOS not Android.
App.cs
using System;
using Xamarin.Forms;
namespace eMO_Xamarin
{
public class App : Application
{
public App ()
{
// The root page of your application
var nav = new NavigationPage (new LoginViewController ());
nav.BarBackgroundColor = Color.FromHex("#EEEEEE");
nav.BarTextColor = Color.FromHex("#424242");
MainPage = nav;
}
}
}
LoginController.cs
using System;
using Xamarin.Forms;
using System.Collections.Generic;
namespace eMO_Xamarin
{
public class LoginViewController : ContentPage
{
Entry userEntry, passwordEntry;
public LoginViewController ()
{
NavigationPage.SetBackButtonTitle (this, "");
NavigationPage.SetHasBackButton (this, false);
this.BackgroundImage = "Bg1.jpg";
userEntry = new Entry () {
HorizontalOptions = LayoutOptions.FillAndExpand,
Placeholder = "Username"
};
passwordEntry = new Entry () {
HorizontalOptions = LayoutOptions.FillAndExpand,
Placeholder = "Password",
IsPassword = true
};
var loginButton = new Button () {
Text = "Login",
TextColor = Color.White,
BackgroundColor = Color.FromHex ("77D065")
};
loginButton.Clicked += OnButtonClickedLogin;
Title = "e-Loan";
this.Padding = new Thickness (50, Device.OnPlatform (20, 0, 0), 50, 20);
stack.Children.Add (appLogoImg);
stack.Children.Add (userEntry);
stack.Children.Add (passwordEntry);
stack.Children.Add (loginButton);
this.Content = scroll;
}
void OnButtonClickedLogin (object sender, EventArgs e)
{
Navigation.InsertPageBefore (new HomeViewController (), this);
Navigation.PopAsync ();
}
}
}
HomeController.cs
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace eMO_Xamarin
{
public class HomeViewController : ContentPage
{
public HomeViewController ()
{
Title = "Welcome John!";
NavigationPage.SetBackButtonTitle (this, "Back");
NavigationPage.SetHasBackButton (this, false);
if (Device.Idiom == TargetIdiom.Phone) {
this.BackgroundImage = "login_home.jpg";
} else if (Device.Idiom == TargetIdiom.Tablet) {
this.BackgroundImage = "Bg6.jpg";
} else {
}
var toolbarItem = new ToolbarItem {
Text = "Logout"
};
toolbarItem.Clicked += OnLogoutButtonClicked;
ToolbarItems.Add (toolbarItem);
Label header = new Label {
Text = "Submitted Loans",
TextColor = Color.Gray,
FontAttributes = FontAttributes.Bold,
FontSize = 30,
HorizontalOptions = LayoutOptions.Center
};
// Create a data template from the type ImageCell
var cell = new DataTemplate (typeof(MenuCell));
ListView listView = new ListView {
ItemsSource = VetData.GetData (),
ItemTemplate = cell, // Set the ImageCell to the item template for the listview
//SeparatorColor = Color.Transparent
};
listView.RowHeight = 75;
listView.BackgroundColor = Color.Transparent;
// Push the list view down below the status bar on iOS.
if (Device.Idiom == TargetIdiom.Phone) {
Padding = new Thickness (10, Device.OnPlatform (20, 0, 0), 0, 0);
} else if (Device.Idiom == TargetIdiom.Tablet) {
Padding = new Thickness (150, Device.OnPlatform (50, 0, 0), 150, 10);
} else {
}
// Set the content for the page.
this.Content = new StackLayout {
Spacing = 20,
Children = {
header,
listView
}
};
listView.ItemSelected += async (sender, e) => {
if (e.SelectedItem != null) {
//do what you want with the selectedItem
// Navigation with back push
await Navigation.PushAsync (new LeadViewController ());
}
//then init the selectedItem of the listview to enable it to be selected again
listView.SelectedItem = null;
};
}
async void OnLogoutButtonClicked (object sender, EventArgs e)
{
// Navigation with out back push
Navigation.InsertPageBefore (new LoginViewController (), this);
await Navigation.PopAsync ();
}
}
}
LeadViewController.cs : MasterDetailScreen : Error facing Screen
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace eMO_Xamarin
{
public class LeadViewController : MasterDetailPage
{
public LeadViewController ()
{
this.BackgroundImage = "Bg6.jpg";
var menuPage = new MenuPage ();
menuPage.OnMenuSelect = (categoryPage) => {
Detail = new NavigationPage (categoryPage);
if (Device.Idiom == TargetIdiom.Phone) {
IsPresented = false;
} else if (Device.Idiom == TargetIdiom.Tablet) {
IsPresented = true;
} else {
IsPresented = true;
}
};
Master = menuPage;
Detail = new NavigationPage (new LetsGetStartedPage ());
MasterBehavior = MasterBehavior.Default;
}
}
}
Your root page is Navigation Page. And you added a MasterDetial Page with Navigation page too.
The similar issue here
Either MasterDetail or Navigation should be the root and not have them inside each other. You can't have 2 navigation pages within each othe
You can have a navigation page as the Detail in a MasterDetail or you can have MasterDetail inside a Navigation Page, but you can't have a navigation page inside a navigation page at any level. This is an Android only restriction but makes it a Xamarin Forms restriction in the end.
Here is your error code:
Detail = new NavigationPage (new LetsGetStartedPage ());
I think you can try the following code:
Detail = new LetsGetStartedPage ();
OR
namespace eMO_Xamarin
{
public class App : Application
{
public App ()
{
var nav = new LoginViewController ();
nav.BarBackgroundColor = Color.FromHex("#EEEEEE");
nav.BarTextColor = Color.FromHex("#424242");
MainPage = nav;
}
}
}
try this code :
listView.ItemSelected += async (sender, e) => {
if (e.SelectedItem != null) {
//do what you want with the selectedItem
// Navigation with back push
await Navigation.PushAsync (new NavigationPage(new LeadViewController ()));
}
//then init the selectedItem of the listview to enable it to be selected again
listView.SelectedItem = null;
};
please make sure that your App.cs has only setting page like this
MainPage = new MasterTestPage();
do not set like this
new NavigationPage(new MasterTestPage());
where MasterTestPage is my MasterDetailPage class
Related
I have a activity with a listView that got items which drive us to another activity called Main Chat. The problem resides when I touch one of those items. Before it loads the respective activity, the background color of the main activity changes to green. I've inspected my code but I can't find any sample of code that does that type of behavior. Here's the video of what happen and the following code:
Video: https://files.fm/u/7nt2szdy#/view/20200213_115056.mp4;play
Sample code of the "click item"
async void OnListItemClicked(object o, ItemTappedEventArgs e)
{
try
{
((ListView)o).SelectedItem = null;
var vListItem = e.Item as Classes.ClassConversation.ResultadoConversation;
var getResposta = await Servicos.Servicos.Token(vListItem.institutionId);
if (getResposta)
await Navigation.PushAsync(new Chat.MainChat(vListItem, user2, true));
}
catch (Exception)
{
await DisplayAlert(Languages.AppResources.Notifications, Languages.AppResources.ErrorOccurred, "Ok");
}
}
Sample code of the Main Chat"
public MainChat (Classes.ClassConversation.ResultadoConversation conversation, Classes.ClassUser.Result user, bool Resposta)
{
InitializeComponent();
//Muda a cor do texto e o local da tab no android
UnselectedTabColor = Color.FromHex("#80FFFFFF");
SelectedTabColor = Color.White;
//Muda a cor do toolbar e texto
if (Resposta == true)
{
((NavigationPage)Xamarin.Forms.Application.Current.MainPage).BackgroundColor = (Color)App.Current.Resources["colorPrimary"];
((NavigationPage)Xamarin.Forms.Application.Current.MainPage).BarBackgroundColor = (Color)App.Current.Resources["colorPrimary"];
((NavigationPage)Xamarin.Forms.Application.Current.MainPage).BarTextColor = (Color)App.Current.Resources["white"];
}
if (conversation.OtherUser == null)
{
conversation.OtherUser = new Classes.ClassConversation.User();
if(conversation.user_From.rowKey == user.rowKey)
{
conversation.OtherUser = conversation.user_To;
}
else
{
conversation.OtherUser = conversation.user_From;
}
}
//Set Name and image of the other user and chat title
if (conversation.OtherUser.photoUri != null)
ImageUser.Source = new UriImageSource { CachingEnabled = true, Uri = conversation.OtherUser.photoUri, CacheValidity = new TimeSpan(800000, 0, 0, 0) };
TitleChat.Text = conversation.title;
NameOtherUser.Text = conversation.OtherUser.name;
if(Device.RuntimePlatform != Device.iOS)
Children.Add(new ChatPage(conversation, user));
else
Children.Add(new ChatPageIos(conversation, user));
Children.Add(new Report(conversation, user));
result = conversation;
}
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);
}
}
I get an IllegalStateException with "activity has been destroyed" when I close my app.
In my App.cs I declare a public static MasterPage:
protected override void OnStart()
{
// Handle when your app starts
if (Device.OS == TargetPlatform.Android)
{
Device.BeginInvokeOnMainThread(() =>
{
masterdetail = new MasterPage();
MainPage = masterdetail;
});
}
else
{
masterdetail = new MasterPage();
MainPage = masterdetail;
}
}
And in the MasterPage.cs I declare the Master and the DetilPage:
public partial class MasterPage : MasterDetailPage
{
public MasterPage()
{
var IsLoggedIn = false;
if (CrossSecureStorage.Current.HasKey("isLoggedIn"))
{
IsLoggedIn = string.Equals(CrossSecureStorage.Current.GetValue("isLoggedIn"), "true", System.StringComparison.CurrentCultureIgnoreCase);
}
Master = SetMasterContentPage();
if (IsLoggedIn)
{
Detail = new NavigationPage(new TaxonomyOverviewPage());
}
else {
Detail = new NavigationPage(new LoginPage());
}
}
ContentPage SetMasterContentPage()
{
var masterPage = new ContentPage { Title = "Test"};
masterPage.Content = new StackLayout
{
Children = {
new Label{Text="Label1"},
new Label{Text="Label2"},
new Label{Text="Label3"}
}
};
return masterPage;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
GC.Collect();
}
}
Ok, that was a bug in Xamarin.Forms Version 2.3.3.175. To fix this bug install an earlier version of Xamarin.Forms. I get my app running with version 2.3.0.107.
The bug in version 2.3.3.175 should be fixed in version 2.3.4-pre1.
I'm using Auth0 with Xamarin Forms PCL Library.
I have following MainPage class :
namespace LoginPattern
{
public class MainPage : MasterDetailPage
{
public MainPage ()
{
Master = new MenuPage ();
Detail = new DetailPage ();
}
}
}
And following in Application class
public App ()
{
Current = this;
Login ();
}
public void ShowMainPage ()
{
MainPage = new MainPage ();
}
public async void Login ()
{
await DependencyService.Get<IAuth0WidgetLogin>().LoginUseAuth0EmbeddedWidget();
App.Current.Properties["IsLoggedIn"] = true;
ShowMainPage ();
}
Hence upon login, I initially I'm not loading any page except the Auth0 Login Widget. Upon the successful login I would like to display the MasterDetailPage. But am getting the following error :
Java.Lang.IllegalArgumentException: DrawerLayout must be measured with MeasureSpec.EXACTLY.
Please advise if I need to load the Widget in an NavigationPage and how to do so.
EDIT 17/7 :
public class MenuPage : ContentPage
{
MasterDetailPage master;
TableView tableView;
public MenuPage ()
{
Title = "LoginPattern";
Icon = "slideout.png";
var section = new TableSection () {
new TextCell {Text = "Sessions"},
new TextCell {Text = "Speakers"},
new TextCell {Text = "Favorites"},
new TextCell {Text = "Room Plan"},
new TextCell {Text = "Map"},
};
var root = new TableRoot () {section} ;
tableView = new TableView ()
{
Root = root,
Intent = TableIntent.Menu,
};
var logoutButton = new Button { Text = "Logout" };
logoutButton.Clicked += (sender, e) => {
App.Current.Logout();
};
Content = new StackLayout {
BackgroundColor = Color.Gray,
VerticalOptions = LayoutOptions.FillAndExpand,
Children = {
tableView,
logoutButton
}
};
}
}
public class DetailPage : ContentPage
{
public DetailPage ()
{
BackgroundColor = new Color (0, 0, 1, 0.2);
var text = "Slide > to see the master / menu";
if (Device.OS == TargetPlatform.Android) {
text = #"Click the action bar dots to see the master / menu";
} else if (Device.OS == TargetPlatform.WinPhone) {
text = #"Click button \/ to see the master / menu ";
}
Content = new StackLayout {
HorizontalOptions = LayoutOptions.Center,
Padding = new Thickness (10, 40, 10, 10),
Children = {
new Label { Text = text }
}
};
}
}
I would try doing two things:
Setting the MainPage of your Application to a blank page (or something like a splash page) before you attempt to display the authentication widget.
Setting an explicit width request on your MenuPage.
Just for assistance to others, following is my final solution :
public class MainPage : MasterDetailPage
{
public MainPage ()
{
Master = new MenuPage (this);
Detail = new DetailPage ();
ShowLoginDialog ();
}
async void ShowLoginDialog()
{
var page = new LoginPage();
await Navigation.PushModalAsync(page);
App.Current.Login ();
await Navigation.PopModalAsync();
}
}
PS: LoginPage is just an empty ContentPage.
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