【问题标题】:OnAppearing different on iOS and AndroidOnAppearing 在 iOS 和 Android 上有所不同
【发布时间】:2018-10-23 18:39:45
【问题描述】:

我发现在 iOS 上,当页面真正出现在屏幕上时调用 OnAppearing,而在 Android 上,它在创建时调用。

我正在使用此事件来懒惰地构造一个昂贵的构造视图,但显然 Android 行为会破坏这一点。

在 Android 上是否有某种方式可以知道屏幕何时出现在屏幕上?

【问题讨论】:

    标签: xamarin xamarin.forms


    【解决方案1】:

    您可以使用该事件:

    this.Appearing += YourPageAppearing;
    

    否则,您应该使用包含生命周期方法的Application 类的方法:

    protected override void OnStart()
    {
        Debug.WriteLine ("OnStart");
    }
    protected override void OnSleep()
    {
        Debug.WriteLine ("OnSleep");
    }
    protected override void OnResume()
    {
        Debug.WriteLine ("OnResume");
    }
    

    【讨论】:

    • Appearing 事件也会在页面真正出现在 Android 上之前触发。我不确定如何使用应用程序类事件来确定页面是否可见。
    • 它在页面构造函数之后和页面被物理绘制时触发。 OnResume 方法是最接近 ViewDidAppear 的方法,这就是我所理解的。你想构建什么?
    • Telerik 日历控件。
    • @IanWarburton 为什么不在 Page 构造函数中简单地初始化它?
    • 创建速度很慢。按下标签页按钮时,我将尝试构建它。
    【解决方案2】:

    在 Android 上,在向用户显示页面视图之前立即调用 Xamarin.Forms.Page.OnAppearing(而不是在“创建”(构造)页面时)。


    如果您希望初始视图快速出现,通过省略昂贵的子视图,使用绑定使该视图的 IsVisible 最初为“false”。这将使其远离可视化树,从而避免构建它的大部分成本。将(不可见)视图放置在网格单元中,其尺寸是恒定的(在 DP 或“*”中 - “自动”以外的任何内容。)因此,当您使其可见时,该布局将为该视图“准备好”。


    方法 1:

    现在您只需要在视图模型中绑定一个将 IsVisible 更改为“true”的绑定。
    最简单的 hack 是在 OnAppearing 中触发一个动作,该动作将在 250 毫秒后更改该变量。


    方法 2:

    干净的替代方法是创建一个自定义页面渲染器,并覆盖“draw”。
    有draw,调用base.draw后,检查页面上的action属性。
    如果不为 null,则调用该操作,然后将其清除(因此只发生一次)。

    我通过从自定义页面基类继承来做到这一点:

    我的每个页面的 XAML(将“ContentPage”更改为“exodus:ExBasePage”):

    <exodus:ExBasePage
        xmlns:exodus="clr-namespace:Exodus;assembly=Exodus"
        x:Class="YourNamespace.YourPage">
        ...
    </exodus:ExBasePage>
    

    xaml.cs:

    using Exodus;
    
    // After creating page, change "ContentPage" to "ExBasePage".
    public partial class YourPage : ExBasePage
    {
    ...
    

    我的自定义内容页面。注意:包括不需要的代码,与 iOS 安全区域和 Android 硬后退按钮相关:

    using Xamarin.Forms;
    using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
    namespace Exodus
    {
        public abstract partial class ExBasePage : ContentPage
        {
            public ExBasePage()
            {
                // Each sub-class calls InitializeComponent(); not needed here.
    
                ExBasePage.SetupForLightStatusBar( this );
            }
    
    
            // Avoids overlapping iOS status bar at top, and sets a dark background color.
            public static void SetupForLightStatusBar( ContentPage page )
            {
                page.On<Xamarin.Forms.PlatformConfiguration.iOS>().SetUseSafeArea( true );
    
                // iOS NOTE: Each ContentPage must set its BackgroundColor to black or other dark color (when using LightContent for status bar).
                //page.BackgroundColor = Color.Black;
                page.BackgroundColor = Color.FromRgb( 0.3, 0.3, 0.3 );
            }
    
    
            // Per-platform ExBasePageRenderer uses these.
            public System.Action NextDrawAction;
    
    
            /// <summary>
            /// Override to do something else (or to do nothing, i.e. suppress back button).
            /// </summary>
            public virtual void OnHardwareBackButton()
            {
                // Normal content page; do normal back button behavior.
                global::Exodus.Services.NavigatePopAsync();
            }
        }
    }
    

    Android 项目中的渲染器:

    using System;
    
    using Android.Content;
    using Android.Views;
    using Android.Graphics;
    
    using Xamarin.Forms;
    using Xamarin.Forms.Platform.Android;
    
    using Exodus;
    using Exodus.Android;
    
    [assembly: ExportRenderer( typeof( ExBasePage ), typeof( ExBasePageRenderer ) )]
    namespace Exodus.Android
    {
        public class ExBasePageRenderer : PageRenderer
        {
            public ExBasePageRenderer( Context context ) : base( context )
            {
            }
    
    
            protected override void OnElementChanged( ElementChangedEventArgs<Page> e )
            {
                base.OnElementChanged( e );
    
                var page = Element as ExBasePage;
                if (page != null)
                    page.firstDraw = true;
            }
    
            public override void Draw( Canvas canvas )
            {
                try
                {
                    base.Draw( canvas );
    
                    var page = Element as ExBasePage;
                    if (page?.NextDrawAction != null)
                    {
                        page.NextDrawAction();
                        page.NextDrawAction = null;
                    }
                }
                catch (Exception ex)
                {
                    // TBD: Got Disposed exception on Android Bitmap, after rotating phone (in simulator).
                    // TODO: Log exception.
                    Console.WriteLine( "ExBasePageRenderer.Draw exception: " + ex.ToString() );
                }
            }
        }
    }
    

    在第一次绘制页面后执行一些操作:

    public partial class YourPage : ExBasePage
    {
    
        protected override void OnAppearing()
        {
            // TODO: OnPlatform code - I don't have it handy.
            // On iOS, we call immediately "DeferredOnAppearing();"
            // On Android, we set this field, and it is done in custom renderer.
            NextDrawAction = DeferredOnAppearing;
        }
    
        void DeferredOnAppearing()
        {
            // Whatever you want to happen after page is drawn first time:
            //   ((MyViewModel)BindingContext).ExpensiveViewVisible = true;
            // Where MyViewModel contains:
            //   public bool ExpensiveViewVisible { get; set; }
            // And your XAML contains:
            //    <ExpensiveView IsVisible={Binding ExpensiveViewVisible}" ... />
        }
    
    }
    

    注意:我在 iOS 上执行此操作的方式不同,因为 iOS 上的 Xamarin Forms(不正确 - 不符合规范)在页面绘制后调用 OnAppearing。
    所以我有 OnPlatform 逻辑。在 iOS 上,OnAppearing 立即调用 DeferredOnAppearing。在 Android 上,显示的行已完成。
    希望 iOS 最终会被修复为调用 OnAppearing BEFORE, 为了两个平台之间的一致性。
    如果是这样,我会为 iOS 添加一个类似的渲染器。

    (当前的 iOS 实现意味着由于弹出导航堆栈,无法在第二次出现之前更新视图。 相反,它出现了过时的内容,那么你就有机会了 纠正它。这不好。)

    【讨论】:

      猜你喜欢
      • 2017-09-27
      • 2023-03-29
      • 2021-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-24
      • 2016-05-03
      • 1970-01-01
      相关资源
      最近更新 更多