Replicating the drop-down ToolbarItem "More" in Xamarin.Forms - android

I am struggling how I could replicate the drop-down ToolbarItem from Xamarin.Forms when a ToolbarItem's order is set to Secondary for IOS, in order for it to look like it does for Android.
Here are some images to better explain what I am looking for:
How it works on Android:
Code:
ToolbarItem toolbarItem = new ToolbarItem()
{
Text = "ToolbarItem",
Order = ToolbarItemOrder.Secondary
};
Images on how it looks on Android:
Image showing the "More" icon
Image showing the "More" icon expanded to show more toolbar items
There is no default "More" icon on the toolbar when setting the Order to Secondary in iOS. Instead what happens, is that a bar below the navigation bar is created, which includes all of the toolbar items - something I do not wish to have for my Application.
This is an example of how it has been achieved before on IOS:
A screenshot I took from one of my Apps that implements this
effect

In native iOS, you can use UIPopoverController to achieve your effect. But please notice that this control can only be used in iPad.
Since you are using Xamarin.Forms, we can create a custom renderer in iOS platform to get this.
Firstly, create a page renderer to display the UIPopoverController. We can show it from a UIBarButtonItem or a UIView depending on your request. Here I use UIBarButtonItem like:
//I defined the navigateItem in the method ViewWillAppear
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
rightItem = new UIBarButtonItem("More", UIBarButtonItemStyle.Plain, (sender, args) =>
{
UIPopoverController popView = new UIPopoverController(new ContentViewController());
popView.PopoverContentSize = new CGSize(200, 300);
popView.PresentFromBarButtonItem(rightItem, UIPopoverArrowDirection.Any, true);
});
NavigationController.TopViewController.NavigationItem.SetRightBarButtonItem(leftItem, true);
}
Secondly, construct the content ViewController in the UIPopoverController(just like the secondary list in android):
public class ContentViewController : UIViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
UITableView tableView = new UITableView(new CGRect(0, 0, 200, 300));
tableView.Source = new MyTableViewSource();
View.AddSubview(tableView);
}
}
public class MyTableViewSource : UITableViewSource
{
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
UITableViewCell cell = tableView.DequeueReusableCell(new NSString("Cell"));
if (cell == null)
{
cell = new UITableViewCell(UITableViewCellStyle.Default, new NSString("Cell"));
}
cell.TextLabel.Text = "Item" + indexPath.Row;
return cell;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return 10;
}
}
At last we can show it on the screen by calling PresentFromBarButtonItem.

Related

How to add a button in Xamarin.Forms?

I'm new to Xamarin.
Could you write me the code for adding a button in Xamarin.Forms?
Maybe you should get started with this introduction. It explains how get started with Xamarin forms.
https://developer.xamarin.com/guides/xamarin-forms/getting-started/introduction-to-xamarin-forms/
public class App : Application
{
public App ()
{
// The root page of your application
MainPage =new ContentPage{
Content= new Button{Text="Hello World",BackgroundColor=Color.Black,HorizontalOptions=LayoutOptions.Center,VerticalOptions=LayoutOptions.Center,TextColor=Color.White}
};
}
}
Above snippet will create a Page with a Button having text Hello World centered horizontally and vertically in the page and set it as the LaunchPage of the application. This is an example of how you can have button programmatically.
There is also the Xamarin forms quick start which runs through an example that adds buttons to the UI.
From example :
xaml:
<Button x:Name="translateButon" Text="Translate" Clicked="OnTranslate" />
bound command in cs:
void OnTranslate (object sender, EventArgs e)
{
translatedNumber = Core.PhonewordTranslator.ToNumber (phoneNumberText.Text);
if (!string.IsNullOrWhiteSpace (translatedNumber)) {
callButton.IsEnabled = true;
callButton.Text = "Call " + translatedNumber;
} else {
callButton.IsEnabled = false;
callButton.Text = "Call";
}
}
Button is something like
Button b = new Button();
Then you should add a button to a layout
StackLayout SL = new StackLayout();
SL.Children.Add(b);
Then set your content page to stacklayout

Codenameone Form adds another title instead of updating when Toolbar added

I'm running into a strange issue when adding a Toolbar to my Form in my Codenameone app. If I set a toolbar on my form, it shows another title with the toolbar hamburger and new title below the title of the previous form instead of replacing it like I would expect. It looks like this:
The functionality works fine replacing the old title like I would expect when I run in the Codenameone simulator, but I get this weird behavior shown in the image when I make an Android build and run it on a Nexus 5 (6.0.1). The back arrow and "12 of 12" is the title from the previousForm
This is my code, am I doing anything wrong here with the Toolbar usage?
void goShowResource(final Form previousForm) {
previous = previousForm;
final Toolbar bar = new Toolbar();
final Form rd = new Form("resource details");
final Resource thisResource = this;
rd.setToolbar(bar);
bar.addCommandToSideMenu(new Command("command 1") {
#Override
public void actionPerformed(ActionEvent evt) {
AddResources ar = new AddResources(settings, thisResource);
ar.goAddResources(rd);
}
});
bar.addCommandToSideMenu(new Command("command 2") {
#Override
public void actionPerformed(ActionEvent evt){
UpdateResource ur = new UpdateResource(settings);
ur.goUpdateResource(rd, thisResource);
}
});
rd.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
showDetails(rd);
rd.show();
}
edit: Additonal info, if I open the sidemenu once, the old title bar at the top shrinks away, and I'm left with the the single correct yet incorrectly formatted title area.
You should use the Toolbar for all the forms in the app or disable the default which is native menu bar when working with the toolbar. You can do the latter by editing the theme and selecting the constants tab then pressing "Add" and selecting commandBehavior=Side.
Android currently defaults to the native ActionBar behavior and Toolbar implicitly overrides that, however when a transition occurs from the native to the lightweight component things can get pretty hairy (and might also look unnatural) so we recommend picking one UI paradigm and going with it.
Since the ActionBar is a volatile API we recommend Toolbar going forward as its far more customizable and gives us a lot of control.
This can be fixed by removing all command from the form after setting the toolbar, then add a fresh back command to the toolbar if required.
void goShowResource(final Form previousForm) {
previous = previousForm;
final Toolbar bar = new Toolbar();
final Form rd = new Form("resource details");
final Resource thisResource = this;
rd.removeAllCommands();
rd.setBackCommand(null);
rd.setToolbar(bar);
//Add back command
Command back = new Command("back") {
#Override
public void actionPerformed(ActionEvent evt) {
previousForm.showBack();
}
};
bar.addCommandToSideMenu(back);
bar.addCommandToSideMenu(new Command("command 1") {
#Override
public void actionPerformed(ActionEvent evt) {
AddResources ar = new AddResources(settings, thisResource);
ar.goAddResources(rd);
}
});
bar.addCommandToSideMenu(new Command("command 2") {
#Override
public void actionPerformed(ActionEvent evt){
UpdateResource ur = new UpdateResource(settings);
ur.goUpdateResource(rd, thisResource);
}
});
rd.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
showDetails(rd);
rd.show();
}

Is it possible to create a custom page layout with Xamarin.Forms?

What I would like to achieve is a bit like this
Where "some view" is some custom content (either a page or content view) that is fixed and never changes (like a status bar) and the page would change by normal navigation (through the NavigationPage).
I already have something similar working but "some view" gets re-created every time the user navigates because every page derives from a base page that contains "some view".
public sealed class RootPage : ExtendedMasterDetailPage
{
public RootPage()
{
// 30%
DrawerWidth = .3f;
Master = new MenuPage();
Detail = new NavigationPage(new HomePage());
}
}
public abstract class MasterPage : ContentPage
{
private readonly Grid _root;
private View _content;
// Subclasses set this property to add their content
public View Detail
{
get { return _content; }
set
{
if (_content != null)
_root.Children.Remove(_content);
_content = value;
if (_content != null)
_root.Children.Add(_content, left: 0, top: 1);
OnPropertyChanged();
}
}
protected MasterPage()
{
_root = new Grid
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
RowDefinitions =
{
new RowDefinition {Height = new GridLength(60, GridUnitType.Absolute)},
new RowDefinition {Height = new GridLength(1, GridUnitType.Star)},
},
RowSpacing = 0
};
// BuildStatusBar() creates the "static" content that is placed above the page
_root.Children.Add(BuildStatusBar(), left: 0, top: 0);
Content = _root;
}
}
public sealed class HomePage : MasterPage
{
public HomePage()
{
// Build content
Detail = ...
}
}
On this blog post the author does something similar to me but in XAML, and he seems fine with his content being reloaded every time; whereas I want that only the content page changes and the header "some view" stays there.
I believe I would need some custom renderer, and the best documentation on that seems to be this swipe to refresh custom renderer. However I cannot figure out how to apply it to my scenario.
I dont think you can that achieve in Xamarin.Forms.
You can change the contents of the content view inside it with animation, but that wont be a right thing to do.
It wont be possible (or too much efforts) to go back through hardware keys.

Xamarin.Forms: wrong button text alignment after click (Android)

I have a problem with Xamarin.Forms (version 1.2.2) on Android (Nexus 5).
The alignment of Button.Text is often not centered after performing a click.
In a short project, I figured out, that updating the UI causes the problem.
public class App
{
public static Page GetMainPage()
{
var label = new Label {
Text = "label",
};
var buttonBad = new Button {
Text = "buttonBad",
Command = new Command(() => label.Text += "1"),
};
var buttonGood = new Button {
Text = "buttonGood",
};
return new ContentPage {
Content = new StackLayout {
Children = {
buttonBad,
buttonGood,
label,
}
}
};
}
}
A click on "buttonBad" (updating the label.Text) causes the text-alignment of this button to not be centered anymore. A click on "buttonGood" does not cause the problem.
Is there a good workaround to solve this problem?
This workaround seems to be too complicated:
http://forums.xamarin.com/discussion/20608/fix-for-button-layout-bug-on-android
edit:
A programatically edit of the UI also cases the bug. Changing the label.Text in an async method after a short waiting leads the "buttonGood" to align its text wrong after a click.
edit2:
I created an example / test project on GitHub:
https://github.com/perpetual-mobile/ButtonTextAlignmentBug.git
The alignment is correct, when the StackLayout is replaced by an AbsolutLayout, but i need the StackLayout to work well.
Ok, after hours of dealing with this silly bug, I resolved it by implementing a custom renderer and overriding ChildDrawableStateChanged:
public override void ChildDrawableStateChanged(Android.Views.View child)
{
base.ChildDrawableStateChanged(child);
Control.Text = Control.Text;
}

Xamarin.Forms swipe gesture recognizer

Xamarin.Forms is very new and very exciting, but for now I see that it has limited documentation and a few samples. I'm trying to make an app with an interface similar to the "MasterDetailPage" one, but also having a right Flyout view, not only the left one.
I've seen that this is not possible using the current API, and so my approach was this:
Create a shared GestureRecognizer interface.
In Android app and iOS in bind this interface to the UIGestureRecognizer on iOS or the OnTouch method on the android.
For iOS this is working but for Android the touch listener over the activity doesn't seem to work.
Is my approach good? Maybe there is another good method to capture touch events directly from the shared code? Or do you have any ideas why the public override bool OnTouchEvent doesn't work in an AndroidActivity?
For Xamarin.Forms swipe gesture recognizer add SwipeGestureRecognizer
<BoxView Color="Teal" ...>
<BoxView.GestureRecognizers>
<SwipeGestureRecognizer Direction="Left" Swiped="OnSwiped"/>
</BoxView.GestureRecognizers>
</BoxView>
Here is the equivalent C# code:
var boxView = new BoxView { Color = Color.Teal, ... };
var leftSwipeGesture = new SwipeGestureRecognizer { Direction = SwipeDirection.Left };
leftSwipeGesture.Swiped += OnSwiped;
boxView.GestureRecognizers.Add(leftSwipeGesture);
For more check here : https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/gestures/swipe
The MasterDetailPage, and other shared elements, are just containers for view renderers to pick up. You best option would be to create a custom LRMasterDetailPage (left-right..) and give it properties for both the DetailLeft and DetailRight. Then you implement a custom ViewRenderer per platform for this custom element.
The element:
public class LRMasterDetailPage {
public View LeftDetail;
public View RightDetail;
public View Master;
}
The renderer:
[assembly:ExportRenderer (typeof(LRMasterDetailPage), typeof(LRMDPRenderer))]
namespace App.iOS.Renderers
{
public class LRMDPRenderer : ViewRenderer<LRMasterDetailPage,UIView>
{
LRMasterDetailPage element;
protected override void OnElementChanged (ElementChangedEventArgs<LRMasterDetailPage> e)
{
base.OnElementChanged (e);
element = e.NewElement;
// Do someting else, init for example
}
protected override void OnElementPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Renderer")
return;
base.OnElementPropertyChanged (sender, e);
if (e.PropertyName == "LeftDetail")
updateLeft();
if (e.PropertyName == "RightDetail")
updateRight();
}
private void updateLeft(){
// Insert view of DetailLeft element into subview
// Add button to open Detail to parent navbar, if not yet there
// Add gesture recognizer for left swipe
}
private void updateRight(){
// same as for left, but flipped
}
}
}
I advise you to use the CarouselView approach, f.e. you can use already existing solutions with carousel view which are supports swipe gestures. So in result your view will be wrapped into this carousel view control

Categories

Resources