2012年7月19日木曜日

表示エリアを計算する

Displayでは画面の高さ・幅を取得しました。これで液晶のサイズはわかりますが、実際にはタイトルバーステータスバーがありますので、使える領域はそれよりも狭くなります。

今回は実際に使える領域を求めるサンプルプログラムをご紹介しましょう。

DisplayTest2aActivity.java
package jp.co.triware.samples.DisplayTest2a;

import android.app.Activity;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class DisplayTest2aActivity extends Activity {
    private final String TAG = this.getClass().getSimpleName();

    private static boolean mbNoTitleBar = false;
    private static boolean mbNoStatusBar = false;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (mbNoTitleBar == true) {
            requestWindowFeature(Window.FEATURE_NO_TITLE);
        }
        setContentView(R.layout.main);
        if (mbNoStatusBar == true) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        }

        Button btnTitleBar = (Button)findViewById(R.id.titlebar_btn);
        btnTitleBar.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mbNoTitleBar = mbNoTitleBar ? false : true;
                Toast.makeText(getApplicationContext(),
                    "TitleBar will be " + (mbNoTitleBar ? "hidden" : "shown"),
                    Toast.LENGTH_SHORT).show();
            }
        });

        Button btnStatusBar = (Button)findViewById(R.id.statusbar_btn);
        btnStatusBar.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mbNoStatusBar = mbNoStatusBar ? false : true;
                if (mbNoStatusBar == true) {
                    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
                } else {
                    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
                }
            }
        });

        Button btnCalc = (Button)findViewById(R.id.calc_btn);
        btnCalc.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                printDisplayInfo();
            }
        });
    }

    private void printDisplayInfo() {
        String buf = "";

        Display display = getWindowManager().getDefaultDisplay();
        buf = "*** Display ***\n";
        buf += "Orientation: " + display.getOrientation() + "\n";
        buf += "Height: " + display.getHeight() + "\n";
        buf += "Width: " + display.getWidth() + "\n";
        buf += "\n";

        Rect rect = new Rect();
        getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
        buf += "*** getWindowVisibleDisplayFrame() ***\n";
        buf += "Top: " + rect.top + "\n";
        buf += "Bottom: " + rect.bottom + "\n";
        buf += "Left: " + rect.left + "\n";
        buf += "Right: " + rect.right + "\n";
        buf += "Height: " + rect.height() + "\n";
        buf += "Width: " + rect.width() + "\n";
        buf += "\n";

        View v = findViewById(Window.ID_ANDROID_CONTENT);
        buf += "*** Window.ID_ANDROID_CONTENT ***\n";
        buf += "Top: " + v.getTop() + "\n";
        buf += "Bottom: " + v.getBottom() + "\n";
        buf += "Left: " + v.getLeft() + "\n";
        buf += "Right: " + v.getRight() + "\n";
        buf += "Height: " + v.getHeight() + "\n";
        buf += "Width: " + v.getWidth() + "\n";

        Log.d(TAG, buf);

        TextView tvResult = (TextView)findViewById(R.id.result_tv);
        tvResult.setText(buf);
    }
}
71~73行めのDisplayクラスのメソッドは以前の「Display」の記事で説明しました。この時はAPI Level 7を対象にしていましたのでgetOrientation()を使いましたが、このメソッドはAPI Level 8 (Android 2.2)以降ではgetRotation()に置き換えられています。Android 2.1以前をターゲットにしないのであれば、getRotation()を使うほうがいいでしょう。getRotation()の戻り値はSurface.ROTATION_0、Surface.ROTATION_90、Surface.ROTATION_180、Surface.ROTATION_270です。getOrientation()では縦方向か横方向しかわかりませんでしたが、getRotation()では180度や270度もわかります。

77行めのgetWindowVisibleDisplayFrame()は、ステータスバーの下がTopの座標になります。高さはその分を差し引いた値となります。

87行めでは、Window.ID_ANDROID_CONTENTを指定してViewを探しています。このViewは、タイトルバーの下にありますので、ViewのTopの座標はタイトルバーの直下になります。高さはその分を差し引いた値となります。

このふたつを組み合わせることで、表示可能なエリアのサイズや、ステータスバーの高さ、タイトルバーの高さを計算することができます。

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <Button
            android:id="@+id/titlebar_btn"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="TitleBar"
            android:layout_weight="1"
            />
        <Button
            android:id="@+id/statusbar_btn"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="StatusBar"
            android:layout_weight="1"
            />
        <Button
            android:id="@+id/calc_btn"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Calc"
            android:layout_weight="1"
            />
    </LinearLayout>

    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        >
        <TextView
            android:id="@+id/result_tv"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            />
    </ScrollView>"
</LinearLayout>
「TitleBar」ボタンはタイトルバーの表示/非表示、「StatusBar」ボタンはステータスバーの表示/非表示を切り替えます。「Calc」ボタンで幅・高さ・開始座標等を求めて表示します。

Androidプロジェクトの設定
プロジェクト名:DisplayTest2a
アプリケーション名:DisplayTest2a
パッケージ名:jp.co.triware.samples.DisplayTest2a
アクティビティーの作成:DisplayTest2aActivity
ビルドターゲットや最小SDKバージョンは、お使いの開発環境に合わせて設定してください。

実行結果
まずは、タイトルバーもステータスバーも表示したままの状態で情報を取得します。アプリを起動した後、「Calc」ボタンをクリックします。
getWindowVisibleDisplayFrame()でステータスバーの直下のY座標が25、Window.ID_ANDROID_CONTENTでタイトルバーの直下のY座標が50となっています。これらのことから、ステータスバーの高さは25ピクセル、タイトルバーの高さも25ピクセルであることがわかります。これで当初の目的は達成しましたが、タイトルバーやステータスバーを消して実際の座標や高さを確認してみましょう。

ではタイトルバーを消します。「TitleBar」ボタンをクリックした後、Backキーを押してホーム画面に戻ってください。サンプルアプリを呼び出して「Calc」ボタンをクリックします。
タイトルバーがないので、Window.ID_ANDROID_CONTENTのViewのTopは0になりました。

次はステータスバーだけ消した場合を試してみます。「TitleBar」ボタンをクリック(タイトルバーを表示させるために)、「StatusBar」ボタンをクリックした後、先程と同じようにアプリを再度呼び出して、「Calc」ボタンをクリックします。
ステータスバーがないので、getWindowVisibleDisplayFrame()のTopは0になりました。
もし、Topが0にならなかったら、画面の縦横を切り替えてみてください。縦長→横長→縦長としてから「Calc」ボタンをクリックします。

註:
エミュレータやGALAXY Nexus (SC-04D)ではアプリ再呼出で意図した結果になりましたが、MEDIAS (N-04C)では縦横を切り替えないと値が変化しませんでした。アプリ再呼出の場合は、もしかするとシステム内部状態が、機種やその時のOSの状態によって違うのかもしれません。
今回はテスト用にアプリ再呼出で値を取得するようにしていますので不都合が生じていますが、通常は、画面生成時や縦横切替時に領域を求めたいでしょうから、問題にはならないと思います。


最後はタイトルバーもステータスバーも消して見ましょう。「TitleBar」ボタンをクリックしたあと、Backキーでホーム画面に戻ってからアプリを再度呼び出してください。
タイトルバーもステータスバーもないので、どちらもTopは0ですね。

最初から実行しなおしたい場合は、システム設定のアプリケーション管理からサンプルプログラムを強制終了させてから始めてください。
Backキーを押してアプリを(見た目は)終了しても、実は裏で動いています。このあたりは別途「Activity Lifecycle」という記事で説明する予定です。

0 件のコメント:

コメントを投稿