Erlend D.
Erlend D.

Reputation: 3073

Toggleable fullscreen with stable layout using Xamarin.Forms/Xamarin.Droid

I'm making an app that must be able to toggle between two view modes:

To test the behaviour of toggling between these two modes, I have created a simple test project that switches between these two modes every two seconds.

The fullscreen mode is working as intended, but there are two issues with the semi-fullscreen mode, as shown in the images below:

  1. The view contents (the Label with five text lines) is pushed down. I want it to remain at the top of the screen, partially hidden behind the system bars.
  2. The action bar is shown behind the status bar. I want it to be shown below the status bar.

What do I need to change in order to get the behaviour I want? (I assume the changes must be made in the ToggleFullscreen function in Page1.xaml.cs or MainActivity.cs)

enter image description here

My code looks like this:

App.xaml.cs

using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace ActionBarTest
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();

            var np = new NavigationPage(new Page1());
            np.Title = "ActionBarTest";
            MainPage = np;
        }
    }
}

Page1.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"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="ActionBarTest.Page1">
    <ContentPage.Content>
        <StackLayout>
            <Label Text="Welcome to Xamarin.Forms!"
                   VerticalOptions="Start" 
                   HorizontalOptions="CenterAndExpand" />
            <Label Text="Line one"
                   VerticalOptions="Start" 
                   HorizontalOptions="CenterAndExpand" />
            <Label Text="Line two"
                   VerticalOptions="Start" 
                   HorizontalOptions="CenterAndExpand" />
            <Label Text="Line three"
                   VerticalOptions="Start" 
                   HorizontalOptions="CenterAndExpand" />
            <Label Text="Line four"
                   VerticalOptions="Start" 
                   HorizontalOptions="CenterAndExpand" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Page1.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace ActionBarTest
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Page1 : ContentPage
    {
        public Page1()
        {
            InitializeComponent();

            Task.Factory.StartNew(() => {
                while (true)
                {
                    Thread.Sleep(2000);
                    ToggleFullscreen(true);
                    Thread.Sleep(2000);
                    ToggleFullscreen(false);
                }
            });
        }
    }

    private void ToggleFullscreen(bool isFullscreen){
        Xamarin.Forms.Device.BeginInvokeOnMainThread(() => { NavigationPage.SetHasNavigationBar(this, !isFullscreen); });
    }
}

MainActivity.cs

using System;
using System.Threading;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace ActionBarTest.Droid
{
    [Activity(Label = "ActionBarTest", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            LoadApplication(new App());

            System.Threading.Tasks.Task.Factory.StartNew(() => {
                while (true)
                {
                    Thread.Sleep(2000);
                    ToggleFullscreen(true);
                    Thread.Sleep(2000);
                    ToggleFullscreen(false);
                }
            });
        }

        private void ToggleFullscreen(bool isFullscreen)
        {
            RunOnUiThread(() =>
            {
                if (isFullscreen)
                {
                    Window.DecorView.SystemUiVisibility = (StatusBarVisibility)(
                        SystemUiFlags.Fullscreen
                        | SystemUiFlags.HideNavigation
                        | SystemUiFlags.Immersive
                        | SystemUiFlags.ImmersiveSticky
                        | SystemUiFlags.LowProfile
                        | SystemUiFlags.LayoutStable
                        | SystemUiFlags.LayoutHideNavigation
                        | SystemUiFlags.LayoutFullscreen
                    );
                }
                else
                {
                    Window.DecorView.SystemUiVisibility = (StatusBarVisibility)(
                        SystemUiFlags.LayoutStable
                        | SystemUiFlags.LayoutHideNavigation
                        | SystemUiFlags.LayoutFullscreen
                    );
                }
            });
        }

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

Upvotes: 0

Views: 1278

Answers (1)

nevermore
nevermore

Reputation: 15816

You can add a Y offset of stackLayout when isFullScreen = false. For example, give a name to stackLayout in Xaml and in the code behiend:

private void ToggleFullscreen(bool isFullscreen)
{
    Xamarin.Forms.Device.BeginInvokeOnMainThread(() => {

        if (isFullscreen)
        {
            myStackLayout.TranslationY = 0;
        }
        else
        {
            myStackLayout.TranslationY = -64;
        }

        NavigationPage.SetHasNavigationBar(this, !isFullscreen);

    });
}

And in your MainActivity, remove the SystemUiFlags.LayoutHideNavigation ,SystemUiFlags.LayoutFullscreen when isFullScreen = false:

private void ToggleFullscreen(bool isFullscreen)
{
    RunOnUiThread(() =>
    {
        if (isFullscreen)
        {
            Window.DecorView.SystemUiVisibility = (StatusBarVisibility)(
                SystemUiFlags.Fullscreen
                | SystemUiFlags.HideNavigation
                | SystemUiFlags.Immersive
                | SystemUiFlags.ImmersiveSticky
                | SystemUiFlags.LowProfile
                | SystemUiFlags.LayoutStable
                | SystemUiFlags.LayoutHideNavigation
                | SystemUiFlags.LayoutFullscreen
            );
        }
        else
        {
            Window.DecorView.SystemUiVisibility = (StatusBarVisibility)(
                SystemUiFlags.LayoutStable
            );
        }
    });
}

I have uploaded a sample and you can check: toggle-model-xamarin.forms

on my phone, the offset had to be -74 -- is there a way of reliably determining it from phone to phone?

You can get the offset by getting the height of status bar and action bar: use a dependency service to get the height first and then set the offset, you can look at this thread about android-status-bar-height-in-xamarin-android.

The design still flashes while it is repositioned -- is there no way of avoiding a reposition at all?

No idea about the flashes, it seems show the view one by one.

I didn't mention that the view has elements that are supposed to stay at the bottom edge of the screen, and when using TranslateY, these are moved upwards as well

Under this situation, you can only set the offset to the view you want to change instead of the whole view.

Upvotes: 0

Related Questions