I use ActionBar tab navigation mode and a ViewPager. This works fine in newer versions of Android, but on Android 4.0.3, tabs is in the top of the ActionBar instead of below.
If I use ActionBar.SetDisplayShowHomeEnabled(true), the problem appears to be fixed. However, up navigation then will not work.
My code looks as follows
ActionBar.SetTitle(Resource.String.DocumentDetails);
ActionBar.SetDisplayHomeAsUpEnabled(true);
ActionBar.NavigationMode = ActionBarNavigationMode.Tabs;
SetContentView(Resource.Layout.Main);
ActionBar.Tab tab = ActionBar.NewTab();
tab.SetText("tab1");
tab.TabSelected += (sender, args) => {
// Do something when tab is selected
};
ActionBar.AddTab(tab);
tab = ActionBar.NewTab();
tab.SetText("tab2");
tab.TabSelected += (sender, args) => {
// Do something when tab is selected
};
ActionBar.AddTab(tab);
Is this on an actual device? I have tested your code in an Emulator and can't reproduce. However, it does seem that several other people reported this to Google: https://code.google.com/p/android/issues/detail?id=36191
So a couple of ideas to fix this. Instead of using SetDisplayHomeAsUpEnabled try using the DisplayOptions property instead:
ActionBar.DisplayOptions = ActionBarDisplayOptions.ShowHome |
ActionBarDisplayOptions.HomeAsUp |
ActionBarDisplayOptions.ShowTitle;
The important one is the ActionBarDisplayOptions.ShowHome in this case, which forces the appearance of a home icon and ActionBarDisplayOptions.HomeAsUp does what SetDisplayHomeAsUpEnabled(true) does. The last one I suppose you can guess.
If that does not help. There was one answer in that issue thread, which suggested to use an extension method to set the tab items as embedded into the ActionBar. However, that will look as if you are running on a tablet. The code is as follows:
using Java.Lang;
using Java.Lang.Reflect;
namespace SomeNamespace
{
public class ActionBarUtils
{
public static void SetHasEmbeddedTabs(Object inActionBar, bool hasEmbeddedTabs)
{
var actionBarClass = inActionBar.Class;
if ("android.support.v7.app.ActionBarImplJBMR2" == actionBarClass.Name)
{
actionBarClass = actionBarClass.Superclass.Superclass;
}
else if ("android.support.v7.app.ActionBarImplJB" == actionBarClass.Name)
{
actionBarClass = actionBarClass.Superclass;
}
try
{
var actionBarField = actionBarClass.GetDeclaredField("mActionBar");
actionBarField.Accessible = true;
inActionBar = actionBarField.Get(inActionBar);
actionBarClass = inActionBar.Class;
}
catch (IllegalAccessException) { }
catch (IllegalArgumentException) { }
catch (NoSuchFieldException) { }
try
{
var method = actionBarClass.GetDeclaredMethod("setHasEmbeddedTabs", new[] { Boolean.Type });
method.Accessible = true;
method.Invoke(inActionBar, new Boolean(hasEmbeddedTabs));
}
catch (NoSuchMethodException) { }
catch (InvocationTargetException) { }
catch (IllegalAccessException) { }
catch (IllegalArgumentException) { }
}
}
}
Then can be used like so in your Activity:
ActionBarUtils.SetHasEmbeddedTabs(ActionBar, true);
Related
I have a content view and left navigation drawer which are commons on 4 pages and Content view contains 4 icons which navigates between 4 different pages. I want to keep my content view and left navigation drawer on all 4 pages.
I create a Master details page and set Master page to Left navigation Drawer and i am changing the details page each time.
I got the exception as android can navigate only page at a time while navigating between multiple pages.
Following is my Rootpage and ContentView Page
public class RootPage : MasterDetailPage
{
LeftNavigationPanel menuPage;
public RootPage(string detailSel)
{
menuPage = new LeftNavigationPanel(); //This is the left navigation class. rename later.
Master = menuPage;
if (detailSel.Equals(""))
{
var detail = new NavigationPage(new Tabpage());
Detail = detail; //homepage
detail.Icon = "leftnav.png";
App.navigation = detail.Navigation;
}
else if (detailSel.Equals("1"))
{
var detail = new NavigationPage(new Post());
Detail = detail; //homepage
detail.Icon = "leftnav.png";
App.navigation = detail.Navigation;
// System.Diagnostics.Debug.WriteLine("page1: " + Navigation.NavigationStack[Navigation.NavigationStack.Count-1]);
}
else if (detailSel.Equals("2"))
{
var detail = new NavigationPage(new TrackTabPage());
Detail = detail; //homepage
detail.Icon = "leftnav.png";
App.navigation = detail.Navigation;
// System.Diagnostics.Debug.WriteLine("page1: " + Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]);
}
}
}
ContentView
public partial class HomeContentView : ContentView
{
public HomeContentView()
{
InitializeComponent();
}
private async void read_click(Object sender, EventArgs e)
{
if (!((Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]) is Tabpage))
{
//await Navigation.PopAsync();
await Navigation.PushAsync(new RootPage(""));
// this.Navigation.PopAsync();
}
}
private async void post_click(Object sender, EventArgs e)
{
if (!((Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]) is Post))
{
System.Diagnostics.Debug.WriteLine("page: "+ Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]);
// await Navigation.PopAsync();
await Navigation.PushAsync(new RootPage("1"));
}
}
private async void track_click(Object sender, EventArgs e)
{
if (!((Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]) is TrackTabPage))
{
System.Diagnostics.Debug.WriteLine("page: " + Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]);
// await Navigation.PopAsync();
await Navigation.PushAsync(new RootPage("2"));
}
}
private void play_click(Object sender, EventArgs e) { }
}
You should use the Navigation from the Detail-Page, because you set the Detail as a new NavigationPage().
I usually use following code to geht the INavigation to navigate inside my app:
public static INavigation GetNavigation()
{
var mdp = Application.Current.MainPage as MasterDetailPage;
if (mdp != null)
{
// Return the navigation of the detail-page
return mdp.Detail.Navigation;
}
var np = Application.Current.MainPage as NavigationPage;
if (np != null)
{
// Page is no MasterDetail-Page, but has a navigation
return np.Navigation;
}
// No navigation found (just set the page, instead of navigation)
return null;
}
And use it like the following example:
var nav = GetNavigation();
if(nav != null)
{
await nav.PushAsync(new Tabpage());
}
else
{
// Just set the page, because there is no navigation
Application.Current.MainPage = new new Tabpage();
// Or like this
Application.Current.MainPage = new NavigationPage(new Tabpage());
}
Update
I changed my answer a little bit (thanks for the comment, I just don't realized this). Do not use new Rootpage("") to change the page. With that you create a new Navigation on top of the current Navigation.. that makes no sense.
Just Push your page onto the current navigation you get by GetNavigation().
Please read the documentation about the navigation pattern in Xamarin.Forms for further informations.
I'm using Xamarin.Forms to create a cross platform application, all of my ContentPages are situated within the PCL.
I'm looking for a way to set and lock the orientation of a single ContentPage to Landscape, preferably without having to create another activity in each of the platform specific projects.
Since my ContentPage.Content is set to a ScrollView, I've tried setting the ScrollOrientation to Horizontal, however this did not work.
I've also tried using a RelativeLayout, but I can't see an Orientation property on this.
public class PlanningBoardView : ContentPage //Container Class.
{
public PlanningBoardView()
{
scroller = new ScrollView ();
Board = new PlanningBoard();
scroller.Orientation = ScrollOrientation.Horizontal;
scroller.WidthRequest = Board.BoardWidth;
scroller.Content = Board;
Content = scroller;
}
}
The last thing I tried was using Xamarin Studio's version of Intellisense and the Xamarin Forms API Doc's to look through the different Layouts available to me, none of which had a Orientation property.
I fear the only way to do this is by creating a second platform specific Activity just for this one ContentPage and setting the orientation to landscape.
Although this method would work, it makes the Navigation between screens a lot more complex.
This is currently being tested in Android.
Hate to say this but this can only be done using custom renderers or a platform-specific code
In android, you can set the RequestedOrientation property of the MainActivity to ScreenOrientation.Landscape.
In iOS, you can override GetSupportedInterfaceOrientations in the AppDelegate class to return one of the UIInterfaceOrientationMask values when Xamarin.Forms.Application.Current.MainPage is the ContentPage that you are intereted in.
Android
[assembly: Xamarin.Forms.ExportRenderer(typeof(MyCustomContentPage), typeof(CustomContentPageRenderer))]
public class CustomContentPageRenderer : Xamarin.Forms.Platform.Android.PageRenderer
{
private ScreenOrientation _previousOrientation = ScreenOrientation.Unspecified;
protected override void OnWindowVisibilityChanged(ViewStates visibility)
{
base.OnWindowVisibilityChanged(visibility);
var activity = (Activity)Context;
if (visibility == ViewStates.Gone)
{
// Revert to previous orientation
activity.RequestedOrientation = _previousOrientation == ScreenOrientation.Unspecified ? ScreenOrientation.Portrait : _previousOrientation;
}
else if (visibility == ViewStates.Visible)
{
if (_previousOrientation == ScreenOrientation.Unspecified)
{
_previousOrientation = activity.RequestedOrientation;
}
activity.RequestedOrientation = ScreenOrientation.Landscape;
}
}
}
iOS
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, UIWindow forWindow)
{
if (Xamarin.Forms.Application.Current == null || Xamarin.Forms.Application.Current.MainPage == null)
{
return UIInterfaceOrientationMask.Portrait;
}
var mainPage = Xamarin.Forms.Application.Current.MainPage;
if (mainPage is MyCustomContentPage ||
(mainPage is NavigationPage && ((NavigationPage)mainPage).CurrentPage is MyCustomContentPage) ||
(mainPage.Navigation != null && mainPage.Navigation.ModalStack.LastOrDefault() is MyCustomContentPage))
{
return UIInterfaceOrientationMask.Landscape;
}
return UIInterfaceOrientationMask.Portrait;
}
}
This can also be done by sending the message from Form project to host project using MessagingCenter class. without using the custom renderer or dependency service as follows,
public partial class ThirdPage : ContentPage
{
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Send(this, "allowLandScapePortrait");
}
//during page close setting back to portrait
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Send(this, "preventLandScape");
}
}
Change in mainactivity to receive the message and set RequestedOrientation
[Activity(Label = "Main", ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation,ScreenOrientation = ScreenOrientation.Portrait)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
//allowing the device to change the screen orientation based on the rotation
MessagingCenter.Subscribe<ThirdPage>(this, "allowLandScapePortrait", sender =>
{
RequestedOrientation = ScreenOrientation.Unspecified;
});
//during page close setting back to portrait
MessagingCenter.Subscribe<ThirdPage>(this, "preventLandScape", sender =>
{
RequestedOrientation = ScreenOrientation.Portrait;
});
}
Check for more in my blog post : http://www.appliedcodelog.com/2017/05/force-landscape-or-portrait-for-single.html
If you are also running into issue on Android where device rotation returns you back to prompt for user email, you can follow up progress of fixes for both ADAL and MSAL here:
https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/issues/1622 https://github.com/xamarin/xamarin-android/issues/3326
Dealdiane's code works well for me with minor change:
protected override void OnWindowVisibilityChanged(ViewStates visibility) {
base.OnWindowVisibilityChanged( visibility );
IRotationLock page = Element as IRotationLock;
if ( page == null )
return;
var activity = (Activity) Context;
if ( visibility == ViewStates.Gone ) {
// Revert to previous orientation
activity.RequestedOrientation = _previousOrientation;
} else if ( visibility == ViewStates.Visible ) {
if ( _previousOrientation == ScreenOrientation.Unspecified ) {
_previousOrientation = activity.RequestedOrientation;
}
switch ( page.AllowRotation() ) {
case RotationLock.Landscape:
activity.RequestedOrientation = ScreenOrientation.SensorLandscape;
break;
case RotationLock.Portrait:
activity.RequestedOrientation = ScreenOrientation.SensorPortrait;
break;
}
}
}
A Hello ,,,
I'm using robotium framework for testing android apps and I used Robotium-actionbarsherlock extension to perform clicks on contextual actionbar menu item ...
Here's the function I'm using ,,,
public void clickOnActionModeOverflowMenuItem(String text) {
Activity activity = solo.getCurrentActivity();
Log.d("aaaa", activity.toString());
if (!(activity instanceof SherlockFragmentActivity)) {
throw new IllegalStateException("This method should be called only in SherlockFragmentActivity.");
}
ActionBarContextView actionBarContextView = null;
try {
ActionBarSherlock actionBarSherlock = (ActionBarSherlock) invokePrivateMethodWithoutParameters(
SherlockFragmentActivity.class, "getSherlock", activity);
Log.d("eeee", actionBarSherlock.toString());
actionBarContextView = (ActionBarContextView) getPrivateField("mActionModeView", actionBarSherlock);
Log.d("dddd", actionBarContextView.toString());
} catch (Exception ex) {
Log.d(LOG_TAG, "Can not find methods to invoke action mode overflow button.");
}
if (actionBarContextView == null) {
Assert.fail("Contextual actionbar is not shown.");
}
actionBarContextView.showOverflowMenu();
sleeper.sleep();
clicker.clickOnText(text, false, 1, true, 0);
}
So, I'm asking about this line ..
actionBarContextView = (ActionBarContextView) getPrivateField("mActionModeView", actionBarSherlock);
How can I know the private field name ?
As the function fails and throws the assertion fail of null actionBarContextView ...
You can download the source code for ActionBarSherlock, which should show you what you are looking for.
I've seen this question:
Changing the ActionBar hide animation?
But it doesn't say whether it's possible to disable animation altogether.
You can now do this,
getSupportActionBar().setShowHideAnimationEnabled(false);
I fixed using the below method:
public static void disableShowHideAnimation(ActionBar actionBar) {
try
{
actionBar.getClass().getDeclaredMethod("setShowHideAnimationEnabled", boolean.class).invoke(actionBar, false);
}
catch (Exception exception)
{
try {
Field mActionBarField = actionBar.getClass().getSuperclass().getDeclaredField("mActionBar");
mActionBarField.setAccessible(true);
Object icsActionBar = mActionBarField.get(actionBar);
Field mShowHideAnimationEnabledField = icsActionBar.getClass().getDeclaredField("mShowHideAnimationEnabled");
mShowHideAnimationEnabledField.setAccessible(true);
mShowHideAnimationEnabledField.set(icsActionBar,false);
Field mCurrentShowAnimField = icsActionBar.getClass().getDeclaredField("mCurrentShowAnim");
mCurrentShowAnimField.setAccessible(true);
mCurrentShowAnimField.set(icsActionBar,null);
}catch (Exception e){
//....
}
}
}
If you use ActionBarSherlock then you can do it. See ActionBarImpl class, it has setShowHideAnimationEnabled(boolean enabled) method.
I have a basic TabActivity. On android 2.1 (and possibly older versions), it looks like a drop shadow is added below the tab widget. On 2.3, this shadow is not present. Is there a way to turn that shadow off completely? Maybe something like "android:fadingEdgeLength=0" ?
Thanks
Are you talking about the white strip? I've hacked past this by calling this method inside onTabChanged
Let you class implement OnTabChangeListener
private static TabHost mTabHost;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Instantiate your tab host normally
}
#Override
public void onTabChanged(String tabId) {
removeWhiteStrip(mTabHost);
}
/**
* Hack
* #param tabHost
*/
private static void removeWhiteStrip(TabHost tabHost) {
TabWidget tw = (TabWidget) tabHost.getChildAt(1);
Field mBottomLeftStrip;
Field mBottomRightStrip;
try {
mBottomLeftStrip = tw.getClass().getDeclaredField("mBottomLeftStrip");
mBottomRightStrip = tw.getClass().getDeclaredField("mBottomRightStrip");
if (!mBottomLeftStrip.isAccessible()) {
mBottomLeftStrip.setAccessible(true);
}
if (!mBottomRightStrip.isAccessible()) {
mBottomRightStrip.setAccessible(true);
}
// This is a blank drawable basically a 1x1 png with 100% alpha
mBottomLeftStrip.set(tw, MyApp.getInstance().getResources().getDrawable(R.drawable.blank));
mBottomRightStrip.set(tw, MyApp.getInstance().getResources().getDrawable(R.drawable.blank));
}
catch (java.lang.NoSuchFieldException e) {
// possibly 2.2
try {
Method stripEnabled = tw.getClass().getDeclaredMethod("setStripEnabled", boolean.class);
stripEnabled.invoke(tw, false);
}
catch (Exception e1) {
e1.printStackTrace();
}
}
catch (Exception e) {
// tut tut shouldn't catch generic exception and ignore it
// but we do because this is a hack
}
}
Enjoy