フリープログラマー日記

iOS,アンドロイド開発を気ままにしながら生きてるおじさんのブログです。

第2回 iOSとAndroidで端末サイズを求める。

古い奴だとお思いでしょうが、
座標をもとに色々やっていく「オジサン世代」には
端末サイズを求めていくのは必要条件でございます。

ということで、端末サイズを求めてみますね。

まずはiOS
前回のNoStoryBoardから呼び出されるViewController.swiftに
サイズを求めるプログラムを書きこんだのがこちら。

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    
    override func viewDidAppear(_ animated: Bool) {
        // UIScreennクラスが端末サイズを持っている
        let width: CGFloat = UIScreen.main.bounds.size.width
        let height: CGFloat = UIScreen.main.bounds.size.height
        
        // UILabel を使って画面中央に表示。
        let info = UILabel()
        info.text = "Width=\(width), Height=\(height)"
        info.sizeToFit()
        info.center = CGPoint(x: width/2, y: height/2)
        self.view.addSubview(info)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

そしてシュミレータで起動したのがこちらです。
背景は白だと見にくいので、
self.window?.backgroundColor = UIColor(red: 1, green: 1, blue: 0.6, alpha: 1)
としてあります。


f:id:momonga117:20180503152606p:plain

ただし、このままでは重大な欠陥があります。
なぜなら、回転を加えた時、再読み込みをしないので、
おかしな表示になってしまいます。


f:id:momonga117:20180503144406p:plainf:id:momonga117:20180503144413p:plain

右側が、縦で起動した後、横にしたものです。
左上隅からの相対位置が変更されず(上の図の左側と同じ位置)に、
内容も元のままで表示されます。

何らかの方法で、情報の更新をし、再描画すればよいのですが、
ひとまずで置いておいて、
次、Android版を作ります。

Androidでは、MainActyvity.javaが最初のファイルとなります。
初期状態はこんな感じです。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

setContentView(R.layout.activity_main); が
xmlファイルをセットするところですが、

ここを空のRelativeLayoutをセットして行きます。

まず、リラティブレイアウトの作成。
名前はMainLayout.javaとしました。
MainLayoutクラスをRelativeLayoutを継承して
作っています。

サイズを求めるのはここに記述します。
getSizeは外部からの呼び出されるようにします。
(実際にはMainActivity.javaからコールされる。)

import android.content.Context;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class MainLayout extends RelativeLayout {

    public MainLayout(Context context) {
        super(context);
    }

    public void getSize(Context context) {

        // 端末のサイズを取得
        final int width = this.getWidth();
        final int height = this.getMeasuredHeight();

        final TextView info = new TextView(context);
        info.setText("Width=" + width + ", Height=" + height );
        RelativeLayout.LayoutParams infoParams = new RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
        );
        infoParams.addRule(CENTER_IN_PARENT,CENTER_IN_PARENT);

        addView(info, infoParams);

    }
}


そして、MainActivity.javaの変更。

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // レイアウトを作成。
        MainLayout mainLayout = new MainLayout(this);

        setContentView(mainLayout);


        // 端末のサイズを求め表示する
        mainLayout.getSize(this);

    }
}

これで、エラーなく走るのですが、実行すると、

width=0 , height=0 が表示されます。

これはAndroidの仕様であって、
描画終了後、レイアウトが確定してからサイズを取る必要があるのです。

そこで、MainActivity.javaを次のように変更。


>|java|
import android.content.Context;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.ViewTreeObserver;

public class MainActivity extends AppCompatActivity {

    private ViewTreeObserver.OnGlobalLayoutListener mListener;
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContext = this;

        // レイアウトを作成。
        final MainLayout mainLayout = new MainLayout(this);

        setContentView(mainLayout);

        // リスナーを設置し、サイズを求める。

        mListener = new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {

                removeListener(mainLayout.getViewTreeObserver(),mListener);

                mainLayout.getSize(mContext);
            }
        };

        mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(mListener);
        
    }

    private static void removeListener(ViewTreeObserver observer, ViewTreeObserver.OnGlobalLayoutListener listner) {
        if (observer == null) {
            return;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            observer.removeOnGlobalLayoutListener(listner);
        } else {
            observer.removeGlobalOnLayoutListener(listner);
        }
    }
}


ViewTreeObserver.OnGlobalLayoutListener というものが必要。
レイアウトが完成したら知らせてね!ってことです。

レイアウトが完成した後実行されるプログラムで、getSizeを呼ぶ。
こうしておくと画面サイズが取れるのです。

しかも、回転してあげると、きちっと表示されているじゃないですか!
回転の対応はiOSよりずっと楽みたいですね。

f:id:momonga117:20180503170953p:plain