I've noticed it's possible to create a mobile app with nested tabs.
Is this possible in Xamarin Forms?
Please see screen shot below:
I can create the bottom tabs on iOS using TabbedPage, but how do I create the nested tabs at the top of the page?
Thank you
The same way you would do on the native app. There are no native nested tabs, so Xamarin can't support it as such thing doesn't exist.
In the native app you have the control at the top (called SegmentedControl on iOS and on Android there is no such control out of the box) where you pick the value and then change the view below manually when it is clicked.
Is this possible in Xamarin Forms?
Yes,of course.You can use CustomRenderer to implement it.Refer to the following code.
in iOS Project . Create a pageRenderer
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using app1;
using app1.iOS;
using UIKit;
using Foundation;
using CoreGraphics;
using ObjCRuntime;
[assembly:ExportRenderer(typeof(MyPage1),typeof(MyPageRenderer))]
namespace app1.iOS
{
public class MyPageRenderer:PageRenderer
{
public MyPageRenderer()
{
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (ViewController != null)
{
NSArray items = NSArray.FromStrings(new string[] { "Courses", "Favourite", "Recent" });
UISegmentedControl segmentedControl = new UISegmentedControl(items)
{
Frame = new CGRect(50, 20, NativeView.Bounds.Width - 100, 35)
};
segmentedControl.SelectedSegment = 0;
segmentedControl.TintColor = UIColor.Red;
segmentedControl.ApportionsSegmentWidthsByContent = true; //Change the width of the segment based on the content of the segment
segmentedControl.AddTarget(this, new Selector("ValueChanged:"), UIControlEvent.ValueChanged);
NativeView.AddSubview(segmentedControl);
}
}
[Export("ValueChanged:")]
void ValueChanged(UISegmentedControl sender)
{
MessagingCenter.Send<Object, int>(this, "ClickSegmentedControl", (int)sender.SelectedSegment);
// switch((int)sender.SelectedSegment){
// case 0:
// break;
// case 1:
// break;
// case 2:
// break;
// default:
// break;
//}
}
}
}
in Forms ,you can subscribe the message .if you want to handle the event in forms when you click the segmented .
public MyPage1()
{
//...
MessagingCenter.Subscribe<Object, int>(this, "ClickSegmentedControl", (sender, arg) =>
{
Console.WriteLine(arg); //arg is num of the segment that you clicked.
});
}
Related
I am currently writing a Messenger app for my Guild, currently I got as far as, I can log in and switch between different tabs, that lists currently dummy message titles (like a whisper name).
My goal later is that once you click on one of the messages you can reply / read messages in WhatsApp style (tips welcome here as well).
But my current issue is that I use the "Bottom" navigation menu. I swap the tabs currently with new activies. But whenever I click a button the screen "flickers" like, it's restarting the whole app.
Is there some way to switch "fluid" the upper part of the app, while the menu always stays nicely at the bottom?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Views;
using Android.Widget;
namespace GuildMaster
{
[Activity(Label = "Whisper")]
public class Whisper : Activity, BottomNavigationView.IOnNavigationItemSelectedListener
{
private ListView whisperlist;
private List<string> itemlist;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_main);
BottomNavigationView navigation = FindViewById<BottomNavigationView>(Resource.Id.navigation);
navigation.SetOnNavigationItemSelectedListener(this);
whisperlist = FindViewById<ListView>(Resource.Id.whisper);
itemlist = new List<string>();
itemlist.Add("Tim");
itemlist.Add("Tom");
ArrayAdapter<string> whisper = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, itemlist);
whisperlist.Adapter = whisper;
whisperlist.ItemClick += Listnames_ItemClick;
// Create your application here
}
public void Listnames_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
Toast.MakeText(this, e.Position.ToString(), ToastLength.Long).Show();
}
public bool OnNavigationItemSelected(IMenuItem item)
{
switch (item.ItemId)
{
case Resource.Id.navigation_home:
StartActivity(typeof(Whisper));
return true;
case Resource.Id.navigation_dashboard:
StartActivity(typeof(Guild));
return true;
case Resource.Id.navigation_notifications:
StartActivity(typeof(Other));
return true;
}
return false;
}
}
}
you should try working with fragments instead of activities with the bottom navigation, working with an activity means u have to start a completely new screen with a new bottom navigation, if that is what you are doing then it is completely normal to "flicker" when doing so
as a solution, use an activity with an xml that holds a fragment view and a bottom navigation view, and when u switch from the bottom navigation in your activity, switch the fragments loaded within the fragment view.
this can be done with different methods, but i would suggest checking out the Navigation Component from Google, since it will also handle the backstack for you.
I'm trying to build a custom EditorWindow in Unity and I want to have the effect like in the picture with the buttons acting like tabs.
I tried to place the buttons horizontally and have negative spaces between them but is not quite the effect I want. Below is my code.
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Heightmap")) { }
GUILayout.Space(-10);
if (GUILayout.Button("Vegetation")) { }
GUILayout.Space(-10);
if (GUILayout.Button("Details")){ }
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
Use GUILayout.Toolbar:
using UnityEngine;
using UnityEditor;
public class MyWindow : EditorWindow
{
int toolbarInt = 0;
string[] toolbarStrings = {"Heightmap", "Vegetation", "Details"};
// Add menu named "My Window" to the Window menu
[MenuItem("Window/My Window")]
static void Init()
{
// Get existing open window or if none, make a new one:
MyWindow window = (MyWindow)EditorWindow.GetWindow(typeof(MyWindow));
window.Show();
}
void OnGUI()
{
toolbarInt = GUILayout.Toolbar(toolbarInt, toolbarStrings);
switch (toolbarInt)
{
case 0:
GUILayout.Button("Content for Heightmap");
break;
case 1:
GUILayout.Button("Content for Vegetation");
break;
case 2:
GUILayout.Button("Button for Details");
break;
}
}
}
I am using Xamarin Forms to make Cross-platform application and I need to create simple view that user can choose date and time, similar to this:
View that I want to create that i found here: Picker in Xamarin iOS. Solution for Android is ready, but I need to create solution for iOS in the same application
I can not use standard date picker and another standard time picker separately from Xamarin Forms. I need to create custom solution (one view - simple choose both date and time).
I have tried to create view in Xamarin Forms that consist of 2 lists in horizontal orientation (one for date, one for time) but when I select one position, the list is not scrolling to the middle of view and also there is not auto-selecting middle position element when I scroll the list up or down. I want to create something that works like Xamarin-iOS solution: "Date and time picker" but in Xamarin Forms.
I have tried also to create in Xamarin-iOS part of project "date and time picker". I have main.storyboard and view controller but I dont know how to display view from Xamarin-iOS inside Xamarin Forms and pass selected date and time.
Can you help me, please?
If you want to implement date-time picker on Xamarin.Forms in iOS platform.You can use CustomRenderer.
in Forms
create a subclass of Picker
public class MyPicker:Picker
{
public MyPicker()
{
}
}
And add it in xaml
<StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<!-- Place new controls here -->
<local:MyPicker WidthRequest="150" BackgroundColor="AliceBlue"/>
</StackLayout>
in iOS
create the renderer of Picker .And you can set the format of picker as you want.
using System;
using Foundation;
using UIKit;
using ObjCRuntime;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using xxx;
using xxx.iOS;
[assembly:ExportRenderer(typeof(MyPicker),typeof(MyPickerRenderer))]
namespace xxx.iOS
{
public class MyPickerRenderer:PickerRenderer
{
string SelectedValue;
public MyPickerRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
SetTimePicker();
}
}
void SetTimePicker()
{
UIDatePicker picker = new UIDatePicker
{
Mode = UIDatePickerMode.DateAndTime
};
picker.SetDate(NSDate.Now,true);
picker.AddTarget(this,new Selector("DateChange:"),UIControlEvent.ValueChanged);
Control.InputView = picker;
UIToolbar toolbar = (UIToolbar)Control.InputAccessoryView;
UIBarButtonItem done = new UIBarButtonItem("Done", UIBarButtonItemStyle.Done, (object sender, EventArgs click) =>
{
Control.Text = SelectedValue;
toolbar.RemoveFromSuperview();
picker.RemoveFromSuperview();
Control.ResignFirstResponder();
MessagingCenter.Send<Object, string>(this, "pickerSelected", SelectedValue);
});
UIBarButtonItem empty = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace, null);
toolbar.Items = new UIBarButtonItem[] { empty, done };
}
[Export("DateChange:")]
void DateChange(UIDatePicker picker)
{
NSDateFormatter formatter = new NSDateFormatter();
formatter.DateFormat = "MM-dd HH:mm aa"; //you can set the format as you want
Control.Text = formatter.ToString(picker.Date);
SelectedValue= formatter.ToString(picker.Date);
MessagingCenter.Send<Object, string>(this,"pickerSelected",SelectedValue);
}
}
}
And use MessagingCenter to pass the selected date and time.
public MainPage()
{
InitializeComponent();
MessagingCenter.Subscribe<Object, string>(this, "pickerSelected", (sender, arg) => {
Console.WriteLine(arg);
//arg is the selected date and time
});
}
I have uploaded the demo on github .You can download it for test.
The effect
If you want to use native views in Xamarin.Forms it is possible, read it here: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/platform/native-views/
I'm trying to refresh a WebView when the user reselects a tab that is already selected on Android coding with Xamarin using deprecated Actionbar and TabHost.
I've got this code
public void OnTabReselected(WhatsOnActivity tab, FragmentTransaction ft)
{
WebView whatsOnWebView = FindViewById<WebView>(Resource.Id.webViewWhatsOn);
//tell webview to reload
whatsOnWebView.Reload();
}
and I've tried to put that code into both my MainActivity and inside My WhatsOnActivity
It doesn't crash the app, but it also doesn't reload the tab. In Xamarin, can I use the "WhatsOnActivity" as the tab in my method? I have a feeling that's what I'm doing wrong.. but if I try to use tab ids, they're not recognized by the IDE. Can anyone give me direction on what I've done wrong?
my complete code can be found here:
https://github.com/hexag0d/BitChute_Mobile_Android_a2/blob/2.68/MainActivity.cs
if you're wondering about the context.
Update
Try to set OnClicklistener on your first tab. Set the listener after you add tabs.
TabHost.TabWidget.GetChildAt(0).SetOnClickListener(new MyOnClickListener(tabHost));
And the listener:
public class MyOnClickListener : Java.Lang.Object, IOnClickListener
{
TabHost tabHost;
public MyOnClickListener(TabHost tabHost)
{
this.tabHost = tabHost;
}
public void OnClick(View v)
{
var parentView = ((ViewGroup)((ViewGroup)tabHost.GetChildAt(0)).GetChildAt(1)).GetChildAt(0);
WebView whatsOnWebView = parentView.FindViewById<WebView>(Resource.Id.webViewWhatsOn);
whatsOnWebView.Reload();
tabHost.CurrentTab = 0;
}
}
And the result is:
Original answer
Please try to add an OnTabChangeListener to your TabHost.
For example, In your MainActivity OnCreate():
TabHost tabHost = FindViewById<TabHost>(Android.Resource.Id.TabHost);
tabHost.SetOnTabChangedListener(new MyOnTabChangedListener(tabHost));
And the MyOnTabChangedListener:
public class MyOnTabChangedListener : Java.Lang.Object, IOnTabChangeListener
{
TabHost tabHost;
public MyOnTabChangedListener(TabHost tabHost)
{
this.tabHost = tabHost;
}
public void OnTabChanged(string tabId)
{
if(tabId == "whats_on")
{
var parentView = ((ViewGroup)((ViewGroup)tabHost.GetChildAt(0)).GetChildAt(1)).GetChildAt(0);
WebView whatsOnWebView = parentView.FindViewById<WebView>(Resource.Id.webViewWhatsOn);
whatsOnWebView.Reload();
}
}
}
First off thank you so much to #Billy Liu - MSFT !!! dude is a lifesaver. He knows his Xamarin. Alright, so here's how you can refresh a tab OnClick
//Assemblies
using System;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Widget;
using Android.Webkit;
using static Android.Widget.TabHost;
using static Android.Views.View;
using Android.Views;
//this assembly is your Activities class, which should contain your class instances
using com.xamarin.example.BitChute.Activities;
EDIT: Put these click listeners inside your MainActivity.cs .. the int inside GetChildAt() represents the tab our listener will respond to. int 0 = tab farthest left and they go up 0 .. 1 .. 2 .. etc .. 1 would mean tab second from the left. Each listener will need it's own listener class instance. I'll show two examples in the next block.
TabHost tabHost = FindViewById<TabHost>(Android.Resource.Id.TabHost);
tabHost.Setup();
tabHost.TabWidget.GetChildAt(0).SetOnClickListener(new WhatsOnClickListener(tabHost));
tabHost.TabWidget.GetChildAt(1).SetOnClickListener(new SubsClickListener(tabHost));
tabHost.TabWidget.GetChildAt(2).SetOnClickListener(new DiscoverClickListener(tabHost));
tabHost.TabWidget.GetChildAt(3).SetOnClickListener(new MyChannelClickListener(tabHost));
tabHost.TabWidget.GetChildAt(4).SetOnClickListener(new SettingsClickListener(tabHost));
EDIT: Create a separate ClickActivity.cs file and then add these listeners into it. you will need to create an OnClickListener instance for each tab. This example is only two of the listeners; but if you're using my template, you will need to use these examples to create another 3 listeners.
public class WhatsOnClickListener : Java.Lang.Object, IOnClickListener
{
TabHost tabHost;
//this int will tell the click listener whether to reload the webview or pop 2 root
static int tbC = 0;
public WhatsOnClickListener(TabHost tabHost)
{
//tell the clicklistener which tabhost to use
this.tabHost = tabHost;
}
//this class handles the click event
public void OnClick(View v)
{
//declare the webview and tell our object where to find the XAML resource
WebView webViewWhatsOn = tabHost.CurrentView.FindViewById<WebView>(Resource.Id.webViewWhatsOn);
//...if the CurrentTab != 0 ... we won't fire the Reload() or LoadUrl()
//..without this logic, the app will crash because our WebViews
//.aren't set to an instance of an object
if (tabHost.CurrentTab == 0)
{
//if tbC int is 0, we will reload the page
if (tbC == 0)
{
//tell whatsOnWebView to Reload
webViewWhatsOn.Reload();
//set the int to one so next time webview will pop to root
tbC = 1;
}
//else if the int is 1, we will pop to root on tab 0
else if (tbC == 1)
{
//tell whatsOnWebView to pop to root
webViewWhatsOn.LoadUrl(#"https://bitchute.com/");
//set the tbC int so that next time webview will reload
tbC = 0;
}
}
//if our current tab isn't zero, we need to set CurrentTab to 0
//this line is critical or our tabs won't work when not selected
tabHost.CurrentTab = 0;
}
}
public class SubsClickListener : Java.Lang.Object, IOnClickListener
{
TabHost tabHost1;
static int tbC = 0;
public SubsClickListener(TabHost tabHost1)
{
this.tabHost1 = tabHost1;
}
public void OnClick(View v)
{
if (tabHost1.CurrentTab == 1)
{
WebView subWebView = tabHost1.CurrentView.FindViewById<WebView>(Resource.Id.webViewSubs);
if (tbC == 0)
{
subWebView.Reload();
tbC = 1;
}
else if (tbC == 1)
{
subWebView.LoadUrl(#"https://bitchute.com/subscriptions/");
tbC = 0;
}
}
tabHost1.CurrentTab = 1;
}
}
hope that helps! I've been trying this for a minute.
I am trying to make it so users can click a certain substring in a label and it would run a method, for example clicking #hashtag would run OpenHashtag(string hashtagand clicking a #taggedUser would run ViewProfile(taggedUser)
I found this tutorial, except I don't want phone numbers or URLs to be clickable, only hashtags and tagged users.
These are the renders its using
Android
[assembly: ExportRenderer(typeof(BodyLabel), typeof(BodyLabelAndroid))]
namespace SocialNetwork.Droid.Renderers
{
public class BodyLabelAndroid : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
var view = (BodyLabel)Element;
if (view == null) return;
TextView textView = new TextView(Forms.Context);
textView.LayoutParameters = new LayoutParams(LayoutParams.WrapContent, LayoutParams.WrapContent);
textView.SetTextColor(view.TextColor.ToAndroid());
// Setting the auto link mask to capture all types of link-able data
textView.AutoLinkMask = MatchOptions.All;
// Make sure to set text after setting the mask
textView.Text = view.Text;
textView.SetTextSize(ComplexUnitType.Dip, (float)view.FontSize);
// overriding Xamarin Forms Label and replace with our native control
SetNativeControl(textView);
}
}
}
IOS
[assembly: ExportRenderer(typeof(BodyLabel), typeof(BodyLabeliOS))]
namespace SocialNetwork.iOS.Renderers
{
public class BodyLabeliOS : ViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
var view = (AwesomeHyperLinkLabel)Element;
if (view == null) return;
UITextView uilabelleftside = new UITextView(new CGRect(0, 0, view.Width, view.Height));
uilabelleftside.Text = view.Text;
uilabelleftside.Font = UIFont.SystemFontOfSize((float)view.FontSize);
uilabelleftside.Editable = false;
uilabelleftside.DataDetectorTypes = UIDataDetectorType.All;
uilabelleftside.BackgroundColor = UIColor.Clear;
SetNativeControl(uilabelleftside);
}
}
}
Android:
Instead of using textView.AutoLinkMask = MatchOptions.All
you can use
Linkify.AddLinks method. Define your regular expression (for example, any word which starts with # or #) and it will work.
But on iOS, it is more complicated I think.
There I see two options:
Use WebView. Parse your string and add "<a href" where needed.
Break your text to pieces and add separate labels for each clickable part. If you want to click only hashtags and tagged users you can add the appropriate labels just below the text. Afterwards you can add tap gesture recognizers to handle the clicks.