画面上にアプリの情報を常時表示する


アプリを開発するとき、Android端末の画面上で常に情報を表示させたいというようなケースもしばしばあるかと思います。

たとえば、Android 4.0の開発者向けオプションで有効にすることができるCPU使用状況の表示機能は
有効にしておくと下図のように端末のCPU状況が常に画面上にオーバーレイ表示されるようになります。

アプリでも同様の事ができればNotificationやToastなどといったしくみ以上に、ユーザに対して情報を効果的に提供することができます。

今回は常に画面上に情報を表示する方法について紹介します。

ただし、後述するように、今回ご紹介するHowToはAndroid端末の画面を上書きする形でアプリのViewを表示するので、使い方を誤ると端末を操作できなくなる可能性があるので、画面設計は慎重に行うようにしましょう。

それではつづきから詳しい解説です。

Androidの表示レイヤー

Androidの画面(View)は複数のレイヤーで構成されており、
これらのレイヤーは情報の重要度・優先度によって使い分けられています。

例えば電話アプリの場合、着信時に着信画面が他のアプリ画面の下に表示されてしまっては
電話を受け取ることができなくなってしまうので、通常のアプリケーションよりも
優先度の高いレイヤーに表示されるようになっています。

表示レイヤーの指定

Viewの表示レイヤーを指定するにはWindowManager.LayoutParamsクラスを使います。

public WindowManager.LayoutParams (int w, int h, int _type, int _flags, int _format)

  • int w : 横幅
  • int h : 高さ
  • int _type : 表示レイヤーを指定
  • int _flags : Viewの挙動/動作について規定(フルスクリーン表示など)
  • int _format : ピクセルフォーマットを指定する

WindowManager.LayoutParamsクラスはViewのレイアウトの属性を設定する場合に使うクラスですが、
引数の_typeでViewの表示レイヤーを指定することができます。

typeに指定することができる値は下表のようになっていますが、実際に使うことができる値は機種やAndroidのバージョンによって異なるようです。

[table “190” not found /]

※他にも設定できる値がありますので、ご興味のある方はAndroid Developersをご参照ください。

画面設計上の注意点

冒頭でも触れたように、表示優先度の高いレイヤーを指定すると端末を操作する事ができなくなる恐れがあります。実用する場合には、Android 4.0のCPU情報のように、他のアプリの操作を邪魔しない位置に表示するように配慮が必要です。

サンプルコード

それではサンプルコードを紹介します。
アプリ上のボタンを押したら画面上に文字列を常時表示するようにします。

 

アプリがアクティブではない状態でも文字列を表示し続けるように、サービスとして処理を実装します。

■src/LayerService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class LayerService extends Service {
    View view;
    WindowManager wm;
 
    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
 
        // Viewからインフレータを作成する
        LayoutInflater layoutInflater = LayoutInflater.from(this);
 
        // 重ね合わせするViewの設定を行う
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSLUCENT);
 
        // WindowManagerを取得する
        wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
 
        // レイアウトファイルから重ね合わせするViewを作成する
        view = layoutInflater.inflate(R.layout.overlay, null);
 
        // Viewを画面上に重ね合わせする
        wm.addView(view, params);
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
 
        // サービスが破棄されるときには重ね合わせしていたViewを削除する
        wm.removeView(view);
    }
 
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }
}

13行目〜18行目でWindowManager.LayoutParamsのオブジェクトを作成し、常時表示するViewの設定を行います。
16行目でTYPE_SYSTEM_OVERLAYを指定し、Viewの表示レイヤーを指定します。
24行目で常時表示するViewのレイアウトファイルからViewのオブジェクトを作成し、27行目で表示します。
44行目のonBindは、サービスがアプリからバインド(紐付け)された場合に呼ばれます。
サービス起動後にもアプリからサービスを操作したい場合にはアプリとサービスをバインドする必要がありますが、
今回はサービスの起動と同時にViewの常時表示を行うだけなのでonBindでは何もしていません。

常時表示用ViewのレイアウトファイルはTextViewを配置しただけのものです。

■res/overlay.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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" >
 
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello! TechBooster!!!"
        android:textSize="32px" />
 
</LinearLayout>

メインのActivityからはボタンが押されたときにサービスの開始/停止を行うようにします。

■src/SystemOverlayLayer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class SystemOverlayLayerActivity extends Activity {
    Button start_button;
    Button stop_button;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        start_button = (Button) findViewById(R.id.start_button);
        stop_button = (Button) findViewById(R.id.stop_button);
 
        final OnClickListener onStartButton = new OnClickListener() {
            @Override
            public void onClick(View v) {
                startService(new Intent(SystemOverlayLayerActivity.this, LayerService.class));
            }
        };
        start_button.setOnClickListener(onStartButton);
 
        final OnClickListener onStopButton = new OnClickListener() {
            @Override
            public void onClick(View v) {
                stopService(new Intent(SystemOverlayLayerActivity.this, LayerService.class));
            }
        };
        stop_button.setOnClickListener(onStopButton);
    }
}

17行目でボタンが押されたときにサービスを開始しViewの常時表示を開始します。
さらに、25行目ではボタンが押されたときにサービスを停止し、Viewの常時表示を停止させています。

また、画面上に独自のViewを重ねるために、以下のパーミッションが必要になります。

■AndroidManifest.xml

1
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

以上、お疲れさまでした。

2 Comments