使用 Xamarin.Forms/Xamarin.Droid 可切换全屏且布局稳定
Toggleable fullscreen with stable layout using Xamarin.Forms/Xamarin.Droid
我正在制作一个必须能够在两种视图模式之间切换的应用程序:
- 全屏:视图应显示在整个屏幕上,没有标题 bar/navigation bar/action 栏。
- Semi-fullscreen:视图仍应填满整个屏幕,但 title/navigation/action 条现在应出现在视图前面,根本无需调整视图大小。
为了测试在这两种模式之间切换的行为,我创建了一个简单的测试项目,每两秒在这两种模式之间切换一次。
全屏模式按预期工作,但 semi-fullscreen 模式存在两个问题,如下图所示:
- 视图内容(有五行文本的Label)被下推。我希望它保留在屏幕顶部,部分隐藏在系统栏后面。
- 操作栏显示在状态栏后面。我想让它显示在状态栏下面。
我需要更改什么才能获得我想要的行为? (我假设必须在 Page1.xaml.cs 或 MainActivity.cs 中的 ToggleFullscreen 函数中进行更改)
我的代码是这样的:
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);
}
}
}
您可以在 isFullScreen = false
时添加 stackLayout
的 Y offset
。例如在Xaml
中给stackLayout
起一个名字,在后面的代码中:
private void ToggleFullscreen(bool isFullscreen)
{
Xamarin.Forms.Device.BeginInvokeOnMainThread(() => {
if (isFullscreen)
{
myStackLayout.TranslationY = 0;
}
else
{
myStackLayout.TranslationY = -64;
}
NavigationPage.SetHasNavigationBar(this, !isFullscreen);
});
}
并且在您的 MainActivity 中,当 isFullScreen = false
:
时删除 SystemUiFlags.LayoutHideNavigation ,SystemUiFlags.LayoutFullscreen
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
);
}
});
}
我已经上传了样本,您可以查看: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?
您可以通过获取 status bar
和 action bar
的高度来获取偏移量:使用 dependency service to get the height first and then set the offset, you can look at this thread about .
The design still flashes while it is repositioned -- is there no way
of avoiding a reposition at all?
不知道闪光灯,好像是一张一张的显示。
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
在这种情况下,您只能将偏移量设置到您要更改的视图而不是整个视图。
我正在制作一个必须能够在两种视图模式之间切换的应用程序:
- 全屏:视图应显示在整个屏幕上,没有标题 bar/navigation bar/action 栏。
- Semi-fullscreen:视图仍应填满整个屏幕,但 title/navigation/action 条现在应出现在视图前面,根本无需调整视图大小。
为了测试在这两种模式之间切换的行为,我创建了一个简单的测试项目,每两秒在这两种模式之间切换一次。
全屏模式按预期工作,但 semi-fullscreen 模式存在两个问题,如下图所示:
- 视图内容(有五行文本的Label)被下推。我希望它保留在屏幕顶部,部分隐藏在系统栏后面。
- 操作栏显示在状态栏后面。我想让它显示在状态栏下面。
我需要更改什么才能获得我想要的行为? (我假设必须在 Page1.xaml.cs 或 MainActivity.cs 中的 ToggleFullscreen 函数中进行更改)
我的代码是这样的:
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);
}
}
}
您可以在 isFullScreen = false
时添加 stackLayout
的 Y offset
。例如在Xaml
中给stackLayout
起一个名字,在后面的代码中:
private void ToggleFullscreen(bool isFullscreen)
{
Xamarin.Forms.Device.BeginInvokeOnMainThread(() => {
if (isFullscreen)
{
myStackLayout.TranslationY = 0;
}
else
{
myStackLayout.TranslationY = -64;
}
NavigationPage.SetHasNavigationBar(this, !isFullscreen);
});
}
并且在您的 MainActivity 中,当 isFullScreen = false
:
SystemUiFlags.LayoutHideNavigation ,SystemUiFlags.LayoutFullscreen
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
);
}
});
}
我已经上传了样本,您可以查看: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?
您可以通过获取 status bar
和 action bar
的高度来获取偏移量:使用 dependency service to get the height first and then set the offset, you can look at this thread about
The design still flashes while it is repositioned -- is there no way of avoiding a reposition at all?
不知道闪光灯,好像是一张一张的显示。
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
在这种情况下,您只能将偏移量设置到您要更改的视图而不是整个视图。