如何在 Xamarin.Forms/XLabs 中处理屏幕 rotation/orientation?

How to handle screen rotation/orientation in Xamarin.Forms/XLabs?

我正在尝试使用此处详述的 XLabs 方法来确定屏幕何时旋转(在 Android 中),但我遇到了问题。

我在 MainActivity

中覆盖了 OnConfigurationChanged 方法
        public override void OnConfigurationChanged (Android.Content.Res.Configuration newConfig)
    {
        base.OnConfigurationChanged (newConfig);

        var xapp = Resolver.Resolve<IXFormsApp> ();
        if (xapp == null)
            return;

        switch (newConfig.Orientation) {
        case Android.Content.Res.Orientation.Landscape:
            xapp.Orientation = XLabs.Enums.Orientation.Landscape;
            break;
        case Android.Content.Res.Orientation.Portrait:
            //xapp.Orientation = XLabs.Enums.Orientation.Portrait;
            break;
        default:
            break;
        }
        }

我在使用 IXFormsApp 中的方向变量时遇到问题,即 xapp.Orientation。 XLabs 文档将其列为 'protected set',编译器也是如此:

MainActivity.cs(109,5,109,21): error CS0200: Property or indexer 'XLabs.Platform.Mvvm.IXFormsApp.Orientation' cannot be assigned to -- it is read only

而且它不会自动设置(当我检查它的使用位置时,它总是设置为 'None'),所以我想知道如何使用它,事实上,如何使用 XLabs/IXFormsApp 确定旋转?

在相关说明中,我还试图设置旋转处理程序(不知道为什么,但我想我会试一试)但结果不正常。

            xapp.Rotation += (sender, args) =>
        {
            switch (args.Value)
            {
            case XLabs.Enums.Orientation.Landscape:
                //xapp.Orientation = XLabs.Enums.Orientation.Landscape;
                ...
                break;
            case XLabs.Enums.Orientation.Portrait:
                ...
                break;
            default:
                break;
            }
            };

如果我在 Android 代码中尝试,我会收到以下错误:

MainActivity.cs(60,4,60,22): error CS0019: Operator '+=' cannot be applied to operands of type 'System.EventHandler<XLabs.EventArgs<XLabs.Enums.Orientation>>' and 'lambda expression'

但是,如果我在表单代码(使用结果的地方)中设置它,就没问题(尽管处理程序似乎从未真正被调用过)。有谁知道为什么会这样?

您可以使用 OnSizeAllocated 方法覆盖来检测方向变化;

double previousWidth;
double previousHeight;

    protected override void OnSizeAllocated(double width, double height)
    {
        base.OnSizeAllocated(width, height);

        if (previousWidth != width || previousHeight != height)
        {
            previousWidth = width;
            previousHeight = height;

            if (width > height)
            {
                // landscape mode
            }
            else
            {
                // portrait mode
            }   
        }
    }

我过去使用过两种不同的解决方案。


首先是创建一个 PageBase class,我的所有页面都继承自它,而 PageBase 继承自常规页面。

我的PageBase有两个抽象方法(所以它的children必须填写),分别是UpdateLandscape和UpdatePortait。 Children 将填写这些方法以根据页面是横向还是纵向布局来布局页面。

正如丹尼尔所说,页面有一个方法 OnSizeAllocated。我让 PageBase 覆盖它并相应地调用 UpdateLandscape 和 UpdatePortait。

如果像您所说的那样,您只是想检查它何时旋转,上面的方法就可以正常工作,因为每当您旋转 phone.

时,页面都会调用 OnSizeAllocated

如果您正在检查横向与纵向是因为您希望您的代码能够随时进行检查,那么下面的第二个解决方案也适用。

我解决的第二种方法是使用依赖服务填充一个IDeviceInfo接口,通过检查DeviceInfo.IsPortait()是true还是false来写所有动态的东西(这样我也让DeviceInfo有宽度和高度,所以我可以随时请求屏幕尺寸)。

在 Android,我填写了我的 Android 代码:

[assembly: Dependency (typeof(Namespace.DeviceInfoProvider))]
namespace Namespace
{
  public class DeviceInfoProvider : IDeviceInfoProvider
  {
    public bool IsPortait () { return DeviceInfoManager.Width < DeviceInfoManager.Height; }
    public int GetWidth () {  return DeviceInfoManager.Width; }
    public int GetHeight () { return DeviceInfoManager.Height; }
  }

  public static class DeviceInfoManager
  {
    public static MainActivity MainActivity { get; set; }
    public static int Width { get { return MainActivity.GetWidth (); } }
    public static int Height { get { return MainActivity.GetHeight (); } }
  }
}

然后在 MainActivity 中我给了它这些方法:

public int GetWidth() {
      return (int)(Resources.DisplayMetrics.WidthPixels / Resources.DisplayMetrics.Density);
    }

    public int GetHeight() {
      return (int)(Resources.DisplayMetrics.HeightPixels / Resources.DisplayMetrics.Density);
    }

而在iOS这边,我是这样填写的:

[assembly: Dependency (typeof(Namespace.DeviceInfoProvider))]
namespace Namespace {

  public class DeviceInfoProvider : IDeviceInfoProvider {
    public bool IsPortait() { return UIScreen.MainScreen.Bounds.Width < UIScreen.MainScreen.Bounds.Height; }

    public int GetWidth() { return (int)UIScreen.MainScreen.Bounds.Width; }

    public int GetHeight() { return (int)UIScreen.MainScreen.Bounds.Height; }
  }
}

就个人而言,我更喜欢用第二种方式编写它并使其检查"if we are in portait mode, here are the differences"。这样纵向和横向没有区别的东西只需要写一次,只有不同的写两次。