SIN@SAPPOROWORKSの覚書

C#を中心に、夜な夜な試行錯誤したコードの記録です。

Xamarin.Forms 画面のサイズを取得する

【 Xamarin 記事一覧 】

コンストラクタでは、サイズが取得できない

次のようなコードを書くと、もれなくサイズの取得に失敗します。
単純にContentPageのサイズ(WidthとHeight)を表示しているだけですが、綺麗に-1になってしまっています。

using Xamarin.Forms;
namespace App1{
    public class App : Application{

        public App(){
            MainPage = new MyPage();
        }
        protected override void OnStart(){}
        protected override void OnSleep(){}
        protected override void OnResume(){}
    }

    class MyPage : ContentPage {

        public MyPage() {
            Content = new Label{ //ラベル1つ配置する
                HorizontalOptions = LayoutOptions.Center,//左右中央に配置
                VerticalOptions = LayoutOptions.Center,//上下中央に配置
                Text = string.Format("Width={0} Height={1}", Width, Height) //ContentPage.Width,ContentPage.Heightプロパティを表示する
            };
        }
    }
}

Android iOS WindowsPhone
f:id:furuya02:20150425053553p:plain:w200 f:id:furuya02:20150425053550p:plain:w200 f:id:furuya02:20150425053552p:plain:w200
-1 * -1 -1 * -1 -1 * -1

OnSizeAllocated

解決方法の1つとして、ContentPageのサイズが取得できてから、使用するという方法があります。
Width,Heightプロパティの値が変化した時点で、OnSizeAllocatedメソッドが呼ばれるので、これをオーバライドする方法です。
まー、この例の場合は、プロパティ値をバインディングした方が手っ取り早いですが・・・

using Xamarin.Forms;
namespace App1{
    public class App : Application{

        public App(){
            MainPage = new MyPage();
        }
        protected override void OnStart(){}
        protected override void OnSleep(){}
        protected override void OnResume(){}
    }

    class MyPage : ContentPage {

        private Label _label = new Label() { //クラス変数とする
            HorizontalOptions = LayoutOptions.Center,//左右中央に配置
            VerticalOptions = LayoutOptions.Center,//上下中央に配置
        };

        public MyPage() {
            Content = _label; //ラベル1つ配置する
        }

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

            label.Text = string.Format("Width={0} Height={1}", Width, Height);//ContentPage.Width,ContentPage.Heightプロパティを表示する
        }
    }
}

下のように、サイズが表示されているのが確認できます。

Android iOS WindowsPhone
f:id:furuya02:20150425053811p:plain:w200 f:id:furuya02:20150425053812p:plain:w200 f:id:furuya02:20150425053814p:plain:w200
320 * 496 320 * 568 480 * 768


コンストラクタの前にサイズを確保する

どうしても、コンストラクタの時点でサイズが欲しい場合・・・
それは、もうAppクラスがインスタンス化される前に、確保するしかありません。

思い切って、Appクラスのコンストラクタを引数ありにして、ディスプレイのサイズを取得するように変更します。

※ここで、ターゲットがContentPageのサイズでなく、ディスプレイのサイズになっていることをご了承下さい。
ContentPageが画面いっぱいに構成されているアプリという前提です。

using Xamarin.Forms;
namespace App1{
    public class App : Application{
        public int ScreenWidth { get; private set; } //スクリーンサイズ(幅)
        public int ScreenHeight { get; private set; }//スクリーンサイズ(高さ)

        public App(int w,int h){
            // パラメータで初期化
            ScreenHeight = h;
            ScreenWidth = w;

            MainPage = new MyPage();
        }
        protected override void OnStart(){}
        protected override void OnSleep(){}
        protected override void OnResume(){}
    }

    class MyPage : ContentPage {

        public MyPage() {
            
            Content = new Label{
                HorizontalOptions = LayoutOptions.Center,
                VerticalOptions = LayoutOptions.Center,
                Text = string.Format("Width={0} Height={1}", ((App)Application.Current).ScreenWidth,((App)Application.Current).ScreenHeight)
            };
        }
    }
}

コンストラクタが引数ありになったので、当然、このままではコンパイルできません。
各プラットフォームで、コンストラクタを生成している部分をいじります。

Android

Androidでは、MainActivity.csを修正します。
Resources.DisplayMetrics.WidthPixels(HeightPixels)でデバイスの実サイズ(dip)を取得し、これをResources.DisplayMetrics.Densityでピクセルに変換しています。

画面サイズを取得して、これをコンストラクタの引数とします。

using Android.App;
using Android.Content.PM;
using Android.OS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

namespace App1.Droid{
    [Activity(Label = "App1", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : FormsApplicationActivity{
        protected override void OnCreate(Bundle bundle){
            base.OnCreate(bundle);

            Forms.Init(this, bundle);
            //スクリーンのサイズを取得する
            var w = (int)(Resources.DisplayMetrics.WidthPixels / Resources.DisplayMetrics.Density);
            var h = (int)(Resources.DisplayMetrics.HeightPixels / Resources.DisplayMetrics.Density);

            
            LoadApplication(new App(w,h));//スクリーンサイズをパラメータにしてAppクラスを生成する
            //LoadApplication(new App());//引数なしはエラーとなる
        }
    }
}

iOS

iOSでは、AppDelegate.csを修正します。
UIScreen.MainScreen.Bounds.Width(Height)で画面サイズを取得して、これをコンストラクタの引数とします。

using Foundation;
using UIKit;

namespace App1.iOS{

    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate{
        public override bool FinishedLaunching(UIApplication app, NSDictionary options){
            global::Xamarin.Forms.Forms.Init();

            //スクリーンのサイズを取得する
            var w = (int)UIScreen.MainScreen.Bounds.Width;
            var h = (int)UIScreen.MainScreen.Bounds.Height;

            LoadApplication(new App(w,h));//スクリーンサイズをパラメータにしてAppクラスを生成する
            //LoadApplication(new App()); //引数なしはエラーとなる
            return base.FinishedLaunching(app, options);
        }
    }
}

WindowsPhone

WindowsPhoneでは、MainPage.xaml.csを修正します。
Application.Current.Host.Content.ActualWidth(ActualHeight)で画面サイズを取得して、これをコンストラクタの引数とします。


using Microsoft.Phone.Controls;
using Xamarin.Forms;
using Xamarin.Forms.Platform.WinPhone;
using Application = System.Windows.Application;

namespace App1.WinPhone{
    public partial class MainPage : FormsApplicationPage{
        public MainPage(){
            InitializeComponent();
            SupportedOrientations = SupportedPageOrientation.PortraitOrLandscape;
            Forms.Init();
            
            //スクリーンのサイズを取得する
            var w = (int)Application.Current.Host.Content.ActualWidth;
            var h = (int)Application.Current.Host.Content.ActualHeight; 
            LoadApplication(new App1.App(w,h));//スクリーンサイズをパラメータにしてAppクラスを生成する
            //LoadApplication(new App1.App());//引数なしはエラーとなる
        }
    }
}


下のように、サイズが表示されているのが確認できます。
ただし、AndroidとWindowsPhoneでは、高さが微妙に違うので注意が必要です。
対象としている領域が違うのが原因です。

Android iOS WindowsPhone
f:id:furuya02:20150425054040p:plain:w200 f:id:furuya02:20150425053812p:plain:w200 f:id:furuya02:20150425054041p:plain:w200
320 * 521 320 * 568 480 * 800



【 Xamarin 記事一覧 】