2010年2月15日月曜日

Andoridの勉強 LinearLayoutタグとRelativeLayoutタグの違いを理解する

「コードからわかるAndroidプログラミングのしくみ 開発で困ったときの解決アプローチ」



の3章では、RestaurantFinderというサンプルアプリケーションを作成しながら、ユーザーインタフェースを理解していく内容になってします。
しかし、多くの人がここで挫折してしまうのではないでしょうか。
なぜなら、2章までの知識では理解できないことが多過ぎるからです。
この業界で仕事をしてから、ひたすらJavaを使って開発をしている私でも悩むことが多かったので、はじめてAndroidの開発をする人は相当悩むのではないでしょうか(私がアホなだけかもしれませんが…)。

ここでは、素直にテキストを読み進めていった人がはまりそうな現象について説明していきます。

1章からテキストの内容を理解していき、サンプルのReviewCriteriaクラスを実装すると、以下の画面(以下UI)が表示されると思います。

画面 1



上で示した画面のjavaソースとmain.xmlファイルの内容を以下に示します。

ReviewCriteriaクラス

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;

public class ReviewCriteria extends Activity {

private static final String CLASSTAG = ReviewCriteria.class.getSimpleName();
private static final int MENU_GET_REVIEWS = Menu.FIRST;
private Spinner cuisine;
private Button grabReviews;
private EditText location;

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

this.location = (EditText) findViewById(R.id.location);
this.cuisine = (Spinner) findViewById(R.id.cuisine);
this.grabReviews = (Button) findViewById(R.id.get_reviews_button);

ArrayAdapter cuisines = new ArrayAdapter(this, R.layout.spinner_view,
getResources().getStringArray(R.array.cuisines));

cuisines.setDropDownViewResource(R.layout.spinner_view);
this.cuisine.setAdapter(cuisines);


}

@Override
/**
* 下部に表示されるメニューを呼び出す
*/
public boolean onCreateOptionsMenu(Menu menu) {

Log.v(Constants.LOGTAG, " " + ReviewCriteria.CLASSTAG + " onCreateOptionsMenu");

super.onCreateOptionsMenu(menu);

// メニューアイテムを追加
menu.add(0, ReviewCriteria.MENU_GET_REVIEWS, 0, "test").setIcon(
android.R.drawable.ic_menu_edit);

return true;
}

@Override
/**
* メニューが選択された際の処理
*/
public boolean onMenuItemSelected(int featureId, MenuItem item) {

switch (item.getItemId()) {
case MENU_GET_REVIEWS:
handleGetRevuews();
return true;
}
return super.onMenuItemSelected(featureId, item);
}

private void handleGetRevuews() {
System.out.println("called!!");
}
}


main.xml

<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<EditText
android:id="@+id/location"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Spinner
android:id="@+id/cuisine"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/get_reviews_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Get Reviews"
/>
</LinearLayout>

上記で示したmain.xmlの内容を見て下さい。
<LinearLayout>タグが使用されています。
つまり、上の画面 1は、LinearLayoutクラスを利用して作成されているということです。

LinearLayoutクラスはViewGroupクラスのサブクラスで、子要素を、水平、あるいは垂直に一直線に並べます。
画面 1のmain.xmlではandroid:orientation="vertical"が指定されています。
android:orientationは、配置する向きを指定する属性です。
ここでは、verticalが設定されています。
verticalは垂直方向に並べます。
画面 1を見ると、EditText、Spinner、Buttonオブジェクト(全てViewのサブクラス)が垂直方向に並べられているのが確認できると思います。
horizontalを設定すると、各オブジェクトが水平方向に並ぶようになります。

しかし、今回のUIの作成では、LinearLayoutクラスでなく、

RelativeLayoutクラス

を使ってUIを作成します。
(もちろん、LinearLayoutクラスでも同じUIを作成できますが、ここではRelativeLayoutクラスを使ったUIの作成方法を理解することが重要です)

RelativeLayoutクラスを使用すると、子要素の位置を、上部、下部、左側といった相対的な位置で指定します。

では、main.xmlをRelativeLayoutに変更してみます。
変更後のmain.xmlファイルは以下のようになります。
main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal"←属性の追加
android:padding="10px"←属性の追加
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<EditText
android:id="@+id/location"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Spinner
android:id="@+id/cuisine"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/get_reviews_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Get Reviews"
/>
</RelativeLayout>

上記のmain.xmlでは、

android:layout_width="fill_parent"
android:layout_height="fill_parent"

は変更しないでそのまま利用しています。

android:orientation="vertical"

は削除しました。

android:gravity="center_horizontal"
android:padding="10px"

は新規に追加した属性です。

それでは、RelativeLayoutタグ内で指定したこれらの属性はどういう意味をもつのでしょうか。
以下に説明を示します。

XML属性
属性 属性の意味 属性変数 属性変数の意味
android:layout_width レイアウトの横幅の長さを指定する属性 fill_parent 縦、又は横に画面いっぱいまでの大きさで描きます
android:layout_height レイアウトの横幅の高さを指定する属性 fill_parent 縦、又は横に画面いっぱいまでの大きさで描きます
android:gravity オブジェクトの配置方法を指定する属性 center_horizontal 左右中央に配置し、サイズ変更を行わないようにしています
android:padding 上下左右のパディングをピクセル単位で設定する属性 10px 10pxに設定しています


では、main.xml変更後のUIを確認してみましょう。

画面 2



「なんじゃこりゃ~~~???????!!!!!!!」
と思わず私は叫びましたね。

正直に言いましょう。最初は原因がわからず、はまりました(アホですな…)。

そこで、グーグル先生に頼って、色々と調べた結果、これは、main.xmlのLinearLayoutタグをRelativeLayoutタグに変更したのが原因です。
って当たり前のことですね。

RelativeLayoutタグを使用する場合は、子要素の位置を、上部、下部、左側といった相対的な位置をきっちりと指定しなければいけません。
変更前は、LinearLayoutタグでandroid:orientation="vertical"属性を指定していたので、位置を指定しなくても、
EditText、Spinner、Buttonオブジェクトが自動的に上から順に並べられていました。
しかし、今回の変更でLinearLayoutタグをRelativeLayoutタグに変更したことで、全てのオブジェクトが同じ位置に配置されてしまったわけです。
つまりは、画面 2で表示されているButtonオブジェクトの下にSpinnerとEditTextが設定されてしまっているのです。
逆にいうと、EditTextオブジェクトの上にSpinnerオブジェクトが配置され、Spinnerオブジェクトの上にButtonオブジェクトが配置されて表示されている状態なのです。

各オブジェクトは、位置をしっかりと指定しないといけません。
まずは、EditTextを表示できるように変更してみましょう。
main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal"
android:padding="10px"
>

<TextView android:id="@+id/location_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10px"
android:layout_marginBottom="15px"
style="@style/label" android:text="@string/location_entry_label" />
←新規でテキストを追加

<EditText
android:id="@+id/location"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/location_label"属性の追加
android:layout_marginLeft="10px"
android:layout_marginBottom="5px"
style="@style/edit_text"
/>
</RelativeLayout>


上記のmain.xmlでは、EditTextの上に、TextViewを配置しました。
追加したTextViewは、文字列などの表示を行う為のコンポーネントタイプのビューです。
ここでは、EditTextの入力内容を説明する文字列「Location(City, ST):」を記述しています。

では、新たに追加・修正したこれらの属性はどういう意味をもつのでしょうか。
今回新たに登場した属性の説明を以下に示します。

XML属性
属性 属性の意味 属性変数 属性変数の意味
android:layout_height レイアウトの横幅の高さを指定する属性 wrap_content 内容に応じて可変の長さになります
android:layout_marginLeft 左方向の余白を指定する属性 10px 10pxに設定しています
android:layout_marginBottom 下方向の余白を指定する属性 15px 15pxに設定しています
android:layout_below 対象のウィジェットを指定したウィジェットの下に配置する属性 @id/location_label TextView@id/location_labelの下に配置しています


では、今回のmain.xml変更後のUIを確認してみましょう。

画面 3



oh!yaeh!いい感じですね。

TextView「Location(City, ST):」の下に、EditTextが表示されていますね。
このように、RelativeLayoutクラスでは、他のViewオブジェクトの位置を規準として画面を組み立てていきます。
規準位置を変更すれば、それに伴って他のViewオブジェクトも自動的に位置が変更できるのが利点です。

では、上記のようにSpinnerオブジェクトとButtonオブジェクトも配置していきましょう。

main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal"
android:padding="10px"
>

<TextView android:id="@+id/location_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10px"
android:layout_marginBottom="15px"
style="@style/label" android:text="@string/location_entry_label" />

<EditText
android:id="@+id/location"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/location_label"
android:layout_marginLeft="10px"
android:layout_marginBottom="5px"
style="@style/edit_text"
/>

<TextView android:id="@+id/cuisine_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/location"
android:layout_marginLeft="10px"
android:layout_marginBottom="5px"
style="@style/label" android:text="@string/cuisine_label" />

<Spinner android:id="@+id/cuisine"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/cuisine_label"
android:layout_marginLeft="10px"
android:layout_marginBottom="20px" />

<Button android:id="@+id/get_reviews_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10px"
android:layout_marginBottom="5px"
android:layout_below="@id/cuisine"
android:text="@string/get_reviews_button_label" />

</RelativeLayout>

main.xmlは上記のように変更しました。今回の変更では、新しい属性は使用してません。
わからない箇所がある場合は、ここまでの内容が理解できていない可能性が高いで、もう一度最初から読みなおしましょう。

では、今回のmain.xml変更後のUIを確認してみましょう。

画面 4



Excellent!完璧ですね。
これで、意図した通りのUIが作成できました。

LinearLayoutタグとRelativeLayoutタグの違いは理解できたでしょうか。
理解できたなら、また新しいことを勉強していきましょう。私も勉強を続けたいと思います。
でわ。

この記事がお役にたちましたらシェアをお願いします

このエントリーをはてなブックマークに追加

1 件のコメント:

  1. 同じところで、はまっていました。
    謎が解けました。
    ありがとうございます!

    返信削除

Related Posts Plugin for WordPress, Blogger...