GUIチュートリアル

AndroidでのGUIの表現方法

AndroidではjavaからGUIクラスを操作してGUIの表現も可能だが、動的にGUIを生成するので無い限りXMLで宣言的にGUIを表現する方が好まれる。そのため、このチュートリアルではXMLを使用して宣言的にGUIを表現する。(EclipseのAndroid向けプラグインでもデフォルトはそうなっている)

また、Androidアプリケーションが起動した直後に表示されるオープニング画面はres/layout/main.xmlに記述されている。以下の章ではGUIのレイアウト方法を説明し、その後GUIからのイベントハンドリング方法を説明する。

レイアウト要素

レイアウト要素は下記の様に使用して子要素のguiアイテム(ボタンやテキスト、画像等)のレイアウト配置ルールを決める。また、レイアウト同士をネストさせて複雑なレイアウトも表現できる

直線レイアウト

垂直方向の直線レイアウト

<?xml version="1.0" encoding="utf-8"?>
<!-- 直線上に並べるレイアウト要素 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="A" />
    <Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="B" />
    <Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="C" />
    <Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="D" />
</LinearLayout>
	

水平方向の直線レイアウト

<?xml version="1.0" encoding="utf-8"?>
<!-- 直線上に並べるレイアウト要素 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="A" />
    <Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="B" />
    <Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="C" />
    <Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="D" />
</LinearLayout>
	

表形式のレイアウト

表形式のレイアウトにはTableLayoutを使用する。一行ずつを示す要素TableRowにその行の要素を入れてやれば良い

<?xml version="1.0" encoding="utf-8"?>
<!-- 表上に並べるレイアウト要素 -->
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="5dip"
    >
    <TableRow>
    	<Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="1-1" />
    	<Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="1-2" />
    	<Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="1-3" />
    </TableRow>
    <TableRow>
    	<Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="2-1" />
    	<Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="2-2" />
    	<Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="2-3" />
    </TableRow>
    <TableRow>
    	<Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="3-1" />
    	<Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="3-2" />
    	<Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="3-3" />
    </TableRow>
</TableLayout>
	

GUI要素共通の属性

layout_height属性はGUI要素の高さを示し、layout_width属性はGUI要素の幅を示す。また、その値がwrap_contentだとGUI要素を表示するための最小のサイズとなり、fill_parentだと親要素の高さ全てを使うサイズとなり、固定値(単位はdipとかspとかpx)だと指定されたサイズとなる

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:text="A"
    >
</Button>
	
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="A"
    >
</Button>
	
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="120dip"
    android:layout_height="200dip"
    android:text="A"
    >
</Button>
	

main.xmlに直接文字列などを入れてしまうと多言語対応する際などには問題になる。そのため、別箇所で定義したリソースを参照する方法をAndroidは提供している。具体的なやり方はres/values/strings.xmlにリファレンスIDと対応する文字列を定義し、参照箇所では@string/リファレンスIDで参照する。(他にもリソースIDの自動生成などで@~と言う方式が使われる。また、@~で宣言したリソースはeclipseのプラグインが自動でjavaソースを生成し、R.~でjava側からも参照可能となる)

res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, GuiTutorial!</string>
    <string name="app_name">GuiTutorial</string>
    <string name="TestReference">リソース参照のテスト</string>
</resources>
	
res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="120dip"
    android:layout_height="200dip"
    android:text="@string/TestReference"
    >
</Button>
	

縦向きレイアウトと横向きレイアウト

Androidは横向きにした際には縦方向と横方向を逆転させる。その際、横向きと縦向きレイアウトを変えたい場合が良くある。Androidではそれの標準的な解決方法として縦向きレイアウトと横向きレイアウトの定義ファイルを別々に分ける事ができる。具体的にはres/layout-land/main.xmlというファイルを横向きレイアウトファイルとして定義すれば良い。

AndroidでのGUIからのイベントハンドリング

これまで、Androidのアプリケーションでアクティビティを作成していなかったが、なぜAndroidのアプリケーションが起動できたのだろうか?eclipseのプラグインが自動でメインのアクティビティを作成していてくれたからだ。自動で作成された以下のソースを見てみよう。まず、Activityクラスを継承したメインアクティビティとなるGuiTutorialクラスを作成し、アクティビティ生成イベントの受信ハンドラ(onCreate)でActivityクラスのデフォルトの処理を実施した後にsetContentViewでmain.xmlで作成したリソースを登録しているだけである。

package com.suddenAngerSystem;

import android.app.Activity;
import android.os.Bundle;

public class GuiTutorial extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}
	

次にAndroidでのイベントの扱いを見る。AndroidではGUIからのイベントの扱いはpushモデルとなる。つまり、GUIにイベントハンドラを登録しておき、イベントが発生したらGUIからイベントハンドラへ通知がなされる。具体的には以下の様にしてイベントハンドラを登録できる。以下ではActivityクラスのfindViewByIdメソッドを使用してViewクラスのインスタンスを取得している。また、その際に使用するIDとしてR.id.TestButtonを使用している。これは後のmain.xmlにて定義するボタンのid(@+id/TextButton)を指定し、生成されたインスタンスを取得する処理になる。(eclipseのプラグインがリソースディレクトリ(res)以下が更新された場合に自動的に対応するjavaソースを生成して@+id/TextButtonをR.id.TextButtonでアクセスできるようにしてくれている。)そして、Android上のGUIオブジェクトはViewクラスの子孫クラスのインスタンスとして表現されるため、取得後Viewクラスのメソッドを使ってイベントハンドラを設定している。

イベントハンドラは単純な関数でアラートボックスという警告を表示するための小さいダイアログを表示させるだけである。

package com.suddenAngerSystem;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.view.View;

public class GuiTutorial extends Activity implements android.view.View.OnClickListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        //リソースIDからボタンのインスタンスを取得
        android.view.View button = this.findViewById(R.id.TestButton);
        //ボタンのイベントハンドラ(クリックのハンドラ)を設定
        button.setOnClickListener(this);
    }

    //イベントが通知されたら単純なアラートボックスを表示する
    @Override
    public void onClick(View v) {
    	AlertDialog.Builder builder = new AlertDialog.Builder(this);
    	builder.setTitle("タイトル");
    	builder.setMessage("メッセージ");
    	builder.setPositiveButton("OK",new android.content.DialogInterface.OnClickListener() {
	        public void onClick(android.content.DialogInterface dialog,int whichButton) {
	            setResult(RESULT_OK);
	        }
	    });
    	builder.create();
    	builder.show();

    }
}
	

main.xmlの定義は以下の様になる。@+id/TextButtonという記載があるが、これはTextButtonというリソース名でリソースIDを自動的に採番して登録すると言う意味となる。Androidでは標準で沢山の種類のGUIアイテムが提供されているが、それについてはeclipseのリソースエディタをみるか、

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
	android:id="@+id/TestButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="ボタン"
    >
</Button>
	

GDIクラスを使用して作成するカスタムビューやビュー拡張のAdapter、テーマ、メニューについては別途説明。

参考サイト

宿題

以下の様なレイアウト、アクションを示すアプリケーションを作成せよ。

縦レイアウト

横レイアウト

アクション

クリックするとボタン文字のタイトルのダイアログを表示

解答例

コメントアウトしてあるので、ソースを確認すること。