記事一覧

AndroidにてカスタムレンダラーがUIをブロックする現象について | Xamarin.Forms


今回は既に完成しているアプリの動作テストをしていて、ListViewが存在するページへの画面遷移が少しフリーズしたように見える原因を調べてみました。大量のソースコードのなかから、原因を特定するには多くの時間を割きましたが、ソースを少しずつ削っていき、特定に至りました。





前提条件
・Windows10 Pro 64Bit
・Visual Studio 2015 Community Update3
・Xamarin 4.7.9.45 (NuGet Xamarin.Forms 2.4.0.282)
・macOS Sierra 10.12.6 / Xcode9 / Xamarin.iOS 11.0.0.0



1.原因

Androidのカスタムレンダラーが原因でした。
まず、PCLで設置したコントロールはAndroidやiOSネイティブなコントロールとは異なり、ネイティブなコントロールとは若干異なる描画・動作をします。レンダラーとはその異なる描画・動作をネイティブなコードで上書きして補正するものです。よって基本的にレンダラーはUIを更新しますので、処理が重すぎるとUIをフリーズさせてしまいます。
今回私の場合ではPCLのListViewにラベルを多く設置し、そのカスタムレンダラーをAndroidプロジェクトに設置して動作を確認すると、大量のラベルとそのカスタムレンダラーが動作し、UIの更新を止めてしまっていました。



2.修正方法

カスタムレンダラーを撤去することが一番簡単な修正方法ですが、目的としてそのコントロールの描画・動作方法を変更する為に設置したと思いますので、その撤去はできないと思います。
そこで、レンダラーの動作を改善するために軽量な動作のコードに補正します。

今回私の例では、ラベルのカスタムフォントの描画で、ラベルごとに毎回フォントを取得してラベルに設定していましたのが、Typeface.CreateFromAssetが重い処理ということに気づき、一度取得したフォントを使いまわすように変更してみたところ、劇的に動作が改善されました。(※全てのラベルのフォントが同じ場合を前提としています。)

【修正前】
[assembly: ExportRenderer(typeof(Label), typeof(AppName.Droid.Renderer.CustomLabelRenderer))]
namespace AppName.Droid.Renderer
{
    public class CustomLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            var fontName = e.NewElement?.FontFamily;
            if (!String.IsNullOrEmpty(fontName))
            {
                var font = Typeface.CreateFromAsset(Forms.Context.Assets, fontName);
                Control.Typeface = font;
            }
        }
    }
}

【修正後】
[assembly: ExportRenderer(typeof(Label), typeof(AppName.Droid.Renderer.CustomLabelRenderer))]
namespace AppName.Droid.Renderer
{
    public class CustomLabelRenderer : LabelRenderer
    {
        //Fontを使いまわす為
        private static Typeface _font = null;

        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            if (_font == null)
            {
                //Fontが取得できていない場合は取得する
                var fontName = e.NewElement?.FontFamily;
                if (!String.IsNullOrEmpty(fontName))
                {
                    _font = Typeface.CreateFromAsset(Forms.Context.Assets, fontName);
                }
            }
            if (_font != null)
            {
                //Fontが取得できていればフォントを設定する
                Control.Typeface = _font;
            }
        }
    }
}

今回ご紹介しましたソースの使用方法の詳細につきましては以前の記事「ラベルやボタンにカスタムフォントを表示する方法」にてご紹介しております。




最後までお読みいただきありがとうございます。
当ブログの内容をまとめた Xamarin逆引きメニュー は以下のURLからご覧になれます。
http://itblogdsi.blog.fc2.com/blog-entry-81.html


関連記事

コメント

コメントの投稿

カテゴリ別記事一覧

広告

プロフィール

石河 純


著者名 :石河 純
自己紹介:素人上がりのIT技術者。趣味は卓球・車・ボウリング

IT関連の知識はざっくりとこんな感じです。
【OS関連】
WindowsServer: 2012/2008R2/2003/2000/NT4
Windows: 10/8/7/XP/2000/me/NT4/98
Linux: CentOS RedHatLinux9
Mac: macOS Sierra 10.12 / OSX Lion 10.7.5 / OSX Snow Leopard 10.6.8
【言語・データベース】
VB.net ASP.NET C#.net Java VBA
Xamarin.Forms
Oracle10g SQLServer2008R2 SQLAnywhere8/11/16
ActiveReport CrystalReport ReportNet(IBM)
【ネットワーク関連】
CCNP シスコ技術者認定
Cisco Catalyst シリーズ
Yamaha RTXシリーズ
FireWall関連
【WEB関連】
SEO SEM CSS IIS6/7 apache2

休みの日は卓球をやっています。
現在、卓球用品通販ショップは休業中です。