2011年10月8日土曜日

Chapter15: 画面用javaファイルの簡単な説明

前回までで、とりあえずレイアウトの実践編は終わりにして、あとはちょっとJavaファイルの説明をしようと思います。とはいっても、あくまでもプログラムをガリガリ書くためということではなく、画面を増やしたりするときのために、必要な知識程度として、です。

なお、説明するのは、たったこれだけの量なので、大丈夫だと思います。
これは、プロジェクトを作った時点で自動的に作られているソースと同じ量です。
public class Chapter11Activity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.chapter11);
    }
    // 省略
}


Activity(画面用ファイル)
Activityとは、画面用のクラスのことです。画面用のJavaファイルは、全てActivityクラスを継承しています。継承とは、指定しているクラスの機能を受け継ぐことを言います。機能(メソッド)の内容を書き換えたり、追記したり、新しい機能を追加したりします。
クラスを継承する場合は、extendsで指定します。
public class Chapter11Activity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.chapter11);
    }
    // 省略
}
とはいっても、新しく画面を追加したいときは、真っ新なファイルを作って、全部書かないといけないわけではありません。Eclipseでファイルを作成した時点で、かなり書いてくれます。

Javaファイル(Activity)の作成方法
まず、パッケージ上で右クリックして、新規 > クラスを選択します。
その後、名前に画面用ファイルの名前を記述します。
今回はTestActivityと書きましたが、別に~Activityで終わる必要はありません。
その後、スーパークラスを指定します。スーパークラスとは、継承元のクラスのことです。今回はActivityを継承します。基本的に指定する場合は、パッケージ名 + クラス名になるのですが、覚えていられないので、Eclipseの力を借ります。アシストです。
Activiくらいまで打った時点で、Ctrl + Spaceを押すと、ズラッと候補が出てきます。Activityを選択しましょう。その後、完了を押します。
これだけです。


次は、onCreateメソッドの説明します。

onCreateメソッド
onCreateメソッドとは、Activityが画面を作る際に自動的に呼ばれるメソッドです。敢えてJavaScriptで例えるならば、Window.onLoadメソッドみたいなものと解釈してください。jQueryで言えば、readyメソッドです(細かい意味まで問われると全然違いますが。読み込み完了後に呼ばれるので)。

継承元のメソッドを上書きする場合は、@Overrideという記述が要ります。
onCreateメソッドは基本的にこの形なので、このままコピー&ペーストしてもらって構いません。

public class Chapter11Activity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.chapter11);
    }
    // 省略
}
Androidに限らずですが、アプリケーションにはライフサイクルがあります。アプリが起動してから終了するまで(一時停止からの復帰含む)に、自動的に呼ばれるメソッドがあります。単に画面の確認をしたいだけだったら、onCreateメソッドだけ覚えておけばいいです。

ライフサイクルについて深く学びたい場合は、TechBoosterさんのライフサイクルの図解を見るとよいでしょう。

では、次にonCreateメソッドの中で何をしているかを説明します。

onCreateメソッドの中でレイアウトファイルを指定
onCreateメソッドの中は2行しかないですね。画面を表示するだけだったら、実はこれだけでよいのです。
public class Chapter11Activity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.chapter11);
    }
    // 省略
}
4行目では、親クラス(ここではActivity)のonCreateメソッドを呼び出しています。この処理は必須です。まぁ色々とやってくれてるんだなぁと思ってください(私もそう思ってます)。なお、superとは、作成のときにスーパークラスという表現があったと思いますが、親クラスを示す特別な変数です。Javaでプログラムしていると、ちょくちょく登場します。

5行目で、この画面で使用するレイアウトXMLを指定しています。setContentViewとは、レイアウトを指定するActivity固有の命令(メソッド)です。ここでは、R.layout.chapter11を指定していますが、これは、/res/layout/chapter11.xmlのことを指します。

これだけです。省略している部分はメニューボタンを押したときに表示されるメニューの動作についてのプログラムなので、あとは省略して、別の機会に取扱います。

以上で、実践編(1)は終わりです。お疲れ様でした。
そして、ありがとうございました。

また実践編(2)でお会いしましょう。

2011年10月7日金曜日

Chapter14: スタイル適用の答え合わせ

Chapter13はできたでしょうか?
スタイルをまとめるというのは、CSSを書く感覚と同じなので、理解しやすかったのではないでしょうか?
では、コードを見てみましょう。Sample03プロジェクトの/res/values/sample_styles.xmlを開いてください。
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="SampleTopBtnPort">
        <item name="android:layout_width">90dip</item>
        <item name="android:layout_height">90dip</item>
        <item name="android:adjustViewBounds">true</item>
    </style>
</resources>
スタイルは、styleタグで定義します。
name属性がCSSのクラス名と同じと思ってください。
各スタイルの定義自身は、itemタグで設定します。
itemタグのname属性に、Viewの属性名を記述し、値を定義します。
敢えてCSSっぽく書くと、
.SampleTopBtnPort {
    android:layout_width: 90dip;
    android:layout_height: 90dip;
    android:adjustViewBounds: true;
}
という感じでしょうか。

これをViewに適用すると、以下のようになります。
/res/layout/chapter14.xmlの一部を抜粋
<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:gravity="top|center_horizontal"
    android:layout_marginBottom="10dp">
    <ImageView
        android:src="@drawable/regist"
        android:layout_marginRight="10dp"
        style="@style/SampleTopBtnPort" />
    <ImageView
        android:src="@drawable/manage"
        android:layout_marginRight="10dp"
        style="@style/SampleTopBtnPort" />
    <ImageView
        android:src="@drawable/photo_book"
        style="@style/SampleTopBtnPort" />
</LinearLayout>
ImageViewのstyle属性に@style/SampleTopBtnPortを指定したことで、だいぶすっきりした見た目になったかと思います。

Chapter13: スタイルにまとめてみる

さて、Chapter12はクリアできたでしょうか?
以下のような画面になっていればOKです。
次は、見た目は変わらないまま、スタイルを作ってみましょう。
スタイルとは、CSSみたいなもので、レイアウトXML内で使うViewの属性をまとめたものです。スタイルを使うことで、レイアウトの修正を簡単にすることができますし、ソースの可読性が上がります。


コードレビュー

では、コードを見てみましょう。Sample03プロジェクトの/res/layout/chapter13.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"
    android:background="@drawable/back_img_repeat">
    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="20dp"
        android:paddingBottom="20dp">
        <ImageView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:src="@drawable/logo"
            android:adjustViewBounds="true"
            android:layout_centerInParent="true" />
    </RelativeLayout>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical"
        android:gravity="bottom"
        android:layout_marginBottom="10dp">
        <!-- 
            ImageViewで定義している属性をstyleに書き換えてください。
            ファイル名はstyles.xml
            style名はTopBtnPortで作成してください。
         -->
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="top|center_horizontal"
            android:layout_marginBottom="10dp">
            <ImageView
                android:src="@drawable/regist"
                android:layout_marginRight="10dp"
                android:layout_width="90dp"
                android:layout_height="90dp"
                android:adjustViewBounds="true" />
            <ImageView
                android:src="@drawable/manage"
                android:layout_marginRight="10dp"
                android:layout_width="90dp"
                android:layout_height="90dp"
                android:adjustViewBounds="true" />
            <ImageView
                android:src="@drawable/photo_book"
                android:layout_width="90dp"
                android:layout_height="90dp"
                android:adjustViewBounds="true" />
        </LinearLayout>
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="top|center_horizontal">
            <ImageView
                android:src="@drawable/ranking"
                android:layout_marginRight="10dp"
                android:layout_width="90dp"
                android:layout_height="90dp"
                android:adjustViewBounds="true" />
            <ImageView
                android:src="@drawable/about"
                android:layout_marginRight="10dp"
                android:layout_width="90dp"
                android:layout_height="90dp"
                android:adjustViewBounds="true" />
            <View
                android:layout_width="90dp"
                android:layout_height="90dp"
                android:adjustViewBounds="true" />
        </LinearLayout>
    </LinearLayout>
    <RelativeLayout
        android:id="@+id/adArea"
        android:layout_width="fill_parent"
        android:layout_height="50dp">
        <ImageView
            android:src="@drawable/nail_banner"
            android:layout_width="fill_parent"
            android:layout_height="50dp"
            android:layout_centerInParent="true" />
    </RelativeLayout>
</LinearLayout>
Chapter12の答え合わせですが、ここではボタンでなくImageViewを使っています。ImageButtonというViewもあるのですが、ボタン用の縁などがつくので、そういうものがいらない場合は、ImageViewで作ってしまったほうが手っ取り早いと思っています。

75行目のViewですが、ここでは表示する画像がないので、敢えて何も表示しないViewを配置しています。何も表示しないViewを配置するというのも、テクニックになってきます(最初思いつきませんでしたが、他の方のソースを見たりして、こういう方法もあるのかと気づきました)。

Chapter13の課題の「スタイルにまとめてみる」ですが、参考になるソースは、/res/layout/chapter14.xmlと、/res/values/sample_styles.xmlになります。参考にして挑戦してみてください。

Chapter12: ボタンを表示させる

さて、Chapter11はクリアできたでしょうか?
以下のような画面になっていればOKです。
次はこの画面にボタンを表示させます。
ボタンといっても、コスメマネージャーの場合はImageViewを使って画像を表示させています。
以下のような画面になります。
課題については、Chapter12のレイアウトファイル内に書かれています。


コードレビュー

では、コードを見てみましょう。Sample03プロジェクトの/res/layout/chapter12.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"
    android:background="@drawable/back_img_repeat">
    <!-- ロゴを入れるRelativeLayout -->
    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="20dp"
        android:paddingBottom="20dp">
        <ImageView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:src="@drawable/logo"
            android:adjustViewBounds="true"
            android:layout_centerInParent="true" />
    </RelativeLayout>
    <!-- メニューを入れるLinearLayout -->
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical">
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="top|center_horizontal"
            android:layout_marginBottom="10dp">
            <!-- 
                ImageViewを3つ使ってボタンを作成してください
                srcは
                @drawable/regist
                @drawable/manage
                @drawable/photo_book
                を使ってください。
                layout_widthは90
                layout_heightは90
                adjustViewBoundsはtrue
                を指定すること。
                他に使う属性もあります。
             -->
        </LinearLayout>
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="top|center_horizontal">
            <!-- 
                ImageViewを2つ使ってボタンを作成してください
                srcは
                @drawable/ranking
                @drawable/about
                を使ってください。
                layout_widthは90
                layout_heightは90
                adjustViewBoundsはtrue
                を指定すること。
                他に使う属性もあります。
                また、もう1つViewを追加して見た目を整えてください。
             -->
        </LinearLayout>
    </LinearLayout>
    <!-- フッター(広告エリア)など -->
    <RelativeLayout
        android:id="@+id/adArea"
        android:layout_width="fill_parent"
        android:layout_height="50dp">
        <ImageView
            android:src="@drawable/nail_banner"
            android:layout_width="fill_parent"
            android:layout_height="50dp"
            android:layout_centerInParent="true" />
    </RelativeLayout>
</LinearLayout>
Chapter11の答え合わせですが、ハイライトしている部分がそうです。
ImageViewで画像を指定する場合、@drawable/[ファイル名]を使います。

余談ですが、このファイルは、/res/drawable-hdpiに置いています。
いろんな解像度に対応しようと思ったら、drawable-****のすべてに画像を置くべきですが、hdpiの画像のみを置いています。 優先順位的に画像が使われるので、mdpiの端末でも、drawable-mdpiに同名のファイルがない場合はdrawable-hdpiにある画像が使われます。 全ての解像度の画像を準備しようと思うと、大変ですし、アプリ自体のファイルサイズも大きくなるので、表示が合わない素材だけを、 特定のフォルダに置くようにしましょう。

 また、デフォルトだとdrawableのフォルダのみ-hdpiや-mdpiという指定がありますが、実はlayoutファイルでもこのような指定をすることができます。詳細は今回は書きませんが、Android Developersの開発ガイドのマルチスクリーンのページを見れば参考になるかと思います。

Chapter12の解答はChapter13に書いてあるので、わからない場合はそちらを見ながら書いてみましょう。

2011年10月6日木曜日

Chapter11: ロゴとフッターを表示させる

さて、Sample03のインポートは終わっているでしょうか?
「タイトルバーを消す」をしている人はもうしているかもしれません。まだの人は、「Sample03プロジェクトを公開しました」より、インポートしてください。

今回の課題からは、実践方式になっています。
今までは基本的にソースコードを読んでもらう方式でしたが、手を動かさないとなかなか覚えられないと思いますので、そうしてみました。
Chapter11の完成コードがChapter12に、Chapter12の完成コードがChapter13に、Chapter13の完成コードがChapter14になっています。

では、Chapter11の最初の画面を見てみましょう。
Sample03アプリを起動しましょう。最初の画面は、完成予定画面です。メニューボタンを押して、chapter11をタップしてみましょう。以下の画面が表示されます。

背景だけ適用されているプレーンな画面です。
Chapter11では、ロゴとフッターをつけてもらいます。
完成イメージは以下になります。
課題はコード内に書かれていますが、コードのみ置いておきます。
Chapter11自体の答え合わせはChapter12で行います。


コードレビュー

では、コードを見てみましょう。Sample03プロジェクトの/res/layout/chapter11.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"
    android:background="@drawable/back_img_repeat">
    <!-- ロゴを入れるRelativeLayout -->
    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="20dp"
        android:paddingBottom="20dp">
        <!-- ImageViewを使って@drawable/logoを入れてください -->
    </RelativeLayout>
    <!-- メニューを入れるLinearLayout -->
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical">
    </LinearLayout>
    <!-- フッター(広告エリア)など -->
    <RelativeLayout
        android:id="@+id/adArea"
        android:layout_width="fill_parent"
        android:layout_height="50dp">
        <!-- ImageViewを使って@drawable/nail_bannerを入れてください -->
    </RelativeLayout>
</LinearLayout>
課題の行をハイライトしています。 回答はChapter12になるので、chapter12.xmlを開いて参考にしてもらっても構いませんが、できればコピペしないでください。

タイトルバーを消す方法

Chapter11にしようかと思いましたが、あまりにも簡単だし、汎用的な内容でもあるので、ChapterではなくTips扱いにします。

Sample03プロジェクトで、コスメマネージャーのトップページを作るというのを題材に扱おうとしていますが、トップページは、アプリによりますが、タイトルバーを消したりする場合があります。コスメマネージャーはトップにロゴがあるので、タイトルバーを消しています。

そこで、タイトルバーを消す方法です。
サンプル画面を見てみましょう。

タイトルバーを消すのは、レイアウトのXMLファイルからではできません。できるのはAndroidManifest.xmlファイルで、です。
AndroidManifest.xmlファイルは、Androidで使うAPIについて定義したり、使う画面用Javaファイルの定義をしたり、アプリのバージョン情報を書いたりする設定ファイルです。

アプリで統一したいテーマを定義する場合はapplicationタグでテーマ属性を定義しますが、
画面(Activity)毎にテーマを設定することも可能です。
画面Aではタイトルバーを表示したいが、画面Bでは消したいということもありますから、今回の例ではActivity単位で消してみます。


コードレビュー

ではレイアウトのコードを見てみましょう。 Sample03プロジェクトの/AndroidManifest.xmlを開いてください。
<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.okolabo.android.sample03"
    android:versionCode="1"
    android:versionName="1.0">
    <uses-sdk android:minSdkVersion="7" />
    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name">
        <activity
            android:name=".SampleActivity"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.NoTitleBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".Chapter11Activity"
            android:theme="@android:style/Theme.NoTitleBar" />
        <activity
            android:name=".Chapter12Activity"
            android:theme="@android:style/Theme.NoTitleBar" />
        <activity
            android:name=".Chapter13Activity"
            android:theme="@android:style/Theme.NoTitleBar" />
        <activity
            android:name=".Chapter14Activity"
            android:theme="@android:style/Theme.NoTitleBar" />
    </application>
</manifest>
最初のactivityタグのandroid:theme属性で、androidがデフォルトで持っているテーマを呼び出しています。android:themeを何も指定しない場合、

@android:style/Theme

が適用されます。今回はタイトルバーを消したかったので、

@android:style/Theme.NoTitleBar

を指定しています。また、タイトルバーだけでなく、ステータスバーも消してフルスクリーンにしたい場合は、

@android:style/Theme.NoTitleBar.FullScreen

を指定します。黒めのテーマを使う場合は@android:style/Theme.Black系を、白めのテーマを使う場合は、@android:style/Theme.Light系を使います。基本の背景色や、メニューボタンを押して出てくるメニューの色などが変わるので、プレーンな感じでいい場合は好きなほうをテーマとして選びましょう(デフォルトは黒系)

なお、ここでは詳しくは解説しませんが、テーマは継承できます。わかりやすくいうと、デフォルトのCSSを使って、気に入らないところだけ自分で上書きして新しいテーマを作ることができるということです。1から全部書かなくてもよいですし、全部自分で書くとAndroidアプリケーションとして他アプリとしての統一性がなくなるので、基本的にはテーマを継承して新しいテーマを定義したほうがよいでしょう。

Sample03プロジェクトを公開しました

前回までで、Sample02のプロジェクトの内容が終わりました。
 次のプロジェクトを準備してあるので、Sample03プロジェクトをダウンロードしてインポートしてください。やり方は、Eclipseでプロジェクトをインポートする(zipのまま)です。ダウンロードするURLの部分だけ、読み替えてください。よろしくお願いします。

Sample03プロジェクトへのリンク(github)

Sample03で扱う内容は、以下の通りです。
今回は著者が公開しているAndroidアプリの「コスメマネージャー」のトップ画面を作ります。

  • タイトルバーを消す
  • (Chapter11)ロゴとフッターを表示させる
  • (Chapter12)ボタンを表示させる
  • (Chapter13)スタイルを作成する
  • (Chapter14)スタイル適用の答え合わせ
  • (Chapter15)画面用javaファイルの簡単な説明

2011年10月4日火曜日

Eclipseのショートカット集

※これは私がよく使うショートカットというだけです。

動作が重たいEclipseが使われる理由として、開発者に便利な機能がたくさんあるからというのがあります。それを使いこなさないと、単に重たい動作のエディタということになってしまいます。

ここでは私が開発のときによく使うショートカットを紹介します。


●Ctrl + Space: アシスト
AdobeのDWでもある機能ですから、アシストの意味はわかるかと思いますが一応。
入力の候補を表示してくれます。この機能のおかげで、タグをあんまり覚えたりしなくてもだいたいわかります。便利です。これがないと開発できない気持ちになってしまうくらいです。
Javaに限らず、xmlファイルでも使うことができます。
<Lを入力した時点で候補が出てきますが、カーソル移動させて表示が消えてしまったときなど、もう一度表示させようと思ったら、Ctrl + Spaceを入力します(Win, Mac共通)。 Macの場合は、Ctrl + SpaceがSpotlightのショートカットに割り当てられているので、それを無効化しておく必要があります。
タグが追加されました。続けて、android:layout_widthを入力したいと思います。
laと入力して、Ctrl + Spaceで、ずらーっと候補が出てきます。
layout_widthを選択後、再びCtrl + Spaceを""の間で押すと候補が表示されます。こんな感じでポンポン選択していくだけでよいことが多いので、コーディング量は相当減ります。

●Ctrl(Command) + Shift + F: ソースコードの整形
ぐちゃぐちゃになったソースコードを見やすい形に整形してくれます。
ただし、機械的に行われるので、コメントを見やすく書いていたつもりでも、それも機械的に整理されてしまうので、気を付けて使う必要がある場合があります。

こんな汚いソースでも・・・
 一発で綺麗に整形してくれます!


●Ctrl(Command) + Shift + O: 必要なパッケージの自動インポート(Java)
プログラムも多少いじらないといけなくなると思いますので、それのサポートとして。プログラマの人は知っておいたほうが超便利です。
Javaのパッケージは膨大にあって、もちろん全部覚えられるわけもなく、長いソースコードになれば、どのパッケージを使っていて、どのパッケージを使わなくなったかなど、知る由もありません。
そこで、Ctrl + Shift + O(オー)を押すと、必要なパッケージの自動インポートを行ってくれます。

下のソースのように、Intentクラスをインポートしていなくてエラーになったり、使わなくなったProgressBarクラスがあったとします。ここで、Ctrl + Shift + Oを押します。
すると、なんということでしょう。
import文が綺麗に整形され、エラーが消え去ったではありませんか。
これも、匠(Eclipse)のなせる業です。

●Ctrl(Command) + 1: クイックフィックス
クイックフィックスは、汎用的に使える機能で、なんか不具合があったら押したら解決する方法を提示してくれたりします。自分が使う場合は、変数の定義の場合によく使います。

まず右辺だけ書きます。今回はnew Intent();です。
その後、行にカーソルを合わせた状態でCtrl + 1を押します。
すると、クイックフィックスの候補が出てきます。新規ローカル変数を定義したいので、1つめを選択します。
 すると、左辺が保管されました。これを知ってから効率がだいぶあがりました。

動作が重い分、使いこなせばかなり効率的にプログラミングや、レイアウトの記述ができるようになります。Eclipseを完全に使いこなす必要はないと思いますが、よく使う機能や便利な機能は知っておいたほうがいいと思ったので、紹介しました!

Chapter10: 単位について(dp, sp, px)

今回はAndroidの単位についてです。
Webデザイナーの方は普段は単位はpxのみ気にしておけば、ほぼ大丈夫かと思いますが(印刷物はdpi気にしますが)Androidの場合は違います。

理由は、端末によって解像度が違うためですね。
日本のAndroidで主流の解像度は以下の通りです。
  • 800x480(hdpi)
  • 854x480(hdpi)
  • 960x640(hdpi)
また、主流ではないですが、以下の解像度の端末もあります。
  • 480x320(mdpi)
  • 320x240(ldpi)
これだけあると、それぞれ専用のレイアウトを準備しないといけなさそうです。実際、準備する場合もあります。マルチスクリーンに関しては、別の機会に書こうと思います。今はシンプルに単位だけに注目します。

Android Developersのサイトに、Supporting Multiple Screensという項目があります。まさに別の機会にとさっき書いたばかりの項目ですが、そこに、単位についても書いてあります。

  • dp(dip: density-independent pixel)
  • sp(scaled pixel)
  • px(pixel)
dp(dip)は、解像度に依存しないpixelです。
解像度に合わせて自動的にpixel変換が行われます。

hdpiの端末は、240dpiです。

240 / 160(基準値) = 1.5px = 1dp

という計算になります。画面上で1.5pxは描けませんが、1dpは描けます。

mdpiの端末は、160dpiです。

160 / 160(基準値) = 1px = 1dp

という計算になります。

これを、px換算の解像度に当てはめると、

800x480(hdpi)の端末だと、533dp x 320dpでした。
480x320(mdpi)の端末だと、480dp x 320dpでした。

なんとdp上だと、横の長さが320dpで一致しましたね。

以下、800x480(hdpi)の端末です。
次は480x320(mdip)の端末(エミュレータ)です。

16dpと書いている文字の大きさはどちらも同じですが、16pxと書いている文字の大きさは明らかに違います。特に、480x320の画面では、16dpも16pxも同じ大きさです。しかし、800x480の画面では違います。16dpの文字の大きさはpxに換算すると16dp x 1.5 = 24px の文字になるからです。

しかし、画面の幅自体は、どちらの端末も320dpです。画面の縦横比が違うので、ちょっと見づらいかもしれませんが、端末自体の大きさ(解像度でなく)が同じならば、文字のサイズは同じはずです。

dpを使えば、画面の解像度を意識する回数が減ります。dpを使っておけば、新しい解像度の端末が出ても、なんとなくちゃんと表示されて動いてくれるということも期待できます(モヤッとした言い回しですが)。

spという単位もありますが、spは文字の解像度設定に倣うという単位なので、基本的にレイアウトにではなく、フォントサイズ指定に使います。フォントサイズ指定ではdpではなくspを使いましょう。

コードレビュー

ではレイアウトのコードを見てみましょう。 Sample02プロジェクトの/res/layout/chapter10.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">
    <!-- 
        dp(dip)は、解像度に合わせてpxが変化する単位です。
        端末の解像度(dpi)に応じて、大きさが変化します。
        基本的にdp(dip)を使ってレイアウトを定義していきます。
        sp(scaled pixels)は主にフォント用の単位です。
        pxで定義する機会は、ほとんどないと言えますが意識する必要はあります。
     -->
    <!-- 普通はtextSizeにはdpは使いませんが、サンプルとして使ってます -->
    <TextView
        android:text="@string/chapter10_dp"
        android:textSize="16dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <!-- 普通はtextSizeにはspを使うのでこっちのが正解 -->
    <TextView
        android:text="@string/chapter10_sp"
        android:textSize="16sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <!-- 敢えてpxで指定してみた例。160dpi端末と240dpi端末で比較するといいです -->
    <TextView
        android:text="@string/chapter10_px"
        android:textSize="16px"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:text="@string/chapter10_description"
        android:textSize="16sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>
textSizeに、各単位(16dp, 16sp, 16px)を指定しています。 解像度の違う端末で見てみると違いがわかりやすいです。なかなか準備できないので、エミュレータで比較してみるのがよいでしょう。

Chapter09: marginとpaddingについて

今回はmarginとpaddingについてです。
marginとpaddingについては、感覚的にはCSSと同じです。ただ、border領域がありません。borderは別途background属性でdrawable(画像もしくは描画を表すxml)指定で表現することになります。

  • padding・・・要素から青枠までの領域(background属性が適用される範囲)
  • margin・・・青枠から赤枠までの領域

marginは他要素からの距離になるので、Root要素(一番最初に定義されるLayoutなりView
なり)にmarginを定義しても、あまり意味がありません(定義できるけど動作が変?)。

marginを定義するのは、何かしらの子要素に対してのみ、行いましょう。


では、サンプルを見てみましょう。
Sample02アプリを起動して、メニューを押してchapter09を選んでください。
以下のような画面になります。
LinearLayoutのhorizontalを3つ準備して

  • marginのみ設定
  • paddingのみ設定
  • marginとpaddingの両方設定

を作ってみました。background属性がわかりやすいように、色を付けています。
パッと見た感じ、わかりにくいので、ちょっと解説用に矢印をつけたバージョンが以下です。
marginのみでTop, Leftなどを別々に指定みてみたところ、marginのみではその通りに反映されました。

ところが、paddingのみで指定したところ、marginのときのようにはなりませんでした。ベースラインがpaddingTopの高いほうに合わせられました。オレンジ枠の部分はmarginでもpaddingでもありません。

marginとpaddingの両方を指定したところ、思った通りの結果ですが、ベースラインはpaddingに合わせられ、marginも反映されました。


コードレビュー

ではレイアウトのコードを見てみましょう。 Sample02プロジェクトの/res/layout/chapter09.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">
    <!-- 
        marginを指定する場合は、layout_marginを使います。
        個別に設定する場合は、
        layout_marginTop,
        layout_marginBottom,
        layout_marginRight,
        layout_marginLeft
        です。
        
        paddingを指定する場合は、paddingを使います。
        個別に設定する場合は、
        paddingTop,
        paddingBottom,
        paddingRight,
        paddingLeft
        です。
        
        CSSのブロック要素と同じく、コンテンツの周囲がpaddingで
        paddingの外側がmarginです。
        marginは、周囲の要素に対して有効なため、
        Root要素(ここでは2行目で定義しているLinearLayout)でmarginを
        定義しても有効ではありません。定義はできますが、動作が不確定ぽいです。
        Root以下の子要素で定義してください。
     -->
    <TextView
        android:text="@string/chapter09_margin"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <!-- marginのみ設定 -->
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:text="@string/app_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:layout_marginLeft="20dp"
            android:background="#008000" />
        <TextView
            android:text="@string/app_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="40dp"
            android:layout_marginLeft="20dp"
            android:background="#4b0082" />
    </LinearLayout>
    <!--
        paddingのみ設定
        より大きな数値でpaddingが設定されている要素にベースラインが合ってしまうようです。
     -->
    <TextView
        android:text="@string/chapter09_padding"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:text="@string/app_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingTop="10dp"
            android:paddingLeft="20dp"
            android:paddingBottom="10dp"
            android:paddingRight="20dp"
            android:background="#008000" />
        <TextView
            android:text="@string/app_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingTop="40dp"
            android:paddingLeft="20dp"
            android:paddingBottom="20dp"
            android:paddingRight="40dp"
            android:background="#4b0082" />
    </LinearLayout>
    <TextView
        android:text="@string/chapter09_margin_padding"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <!-- marginとpaddingを設定 -->
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:text="@string/app_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:paddingTop="10dp"
            android:paddingLeft="20dp"
            android:paddingBottom="10dp"
            android:paddingRight="20dp"
            android:background="#008000" />
        <TextView
            android:text="@string/app_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:paddingTop="40dp"
            android:paddingLeft="20dp"
            android:paddingBottom="20dp"
            android:paddingRight="40dp"
            android:background="#4b0082" />
    </LinearLayout>
</LinearLayout>
marginは以下のようにして指定します。
  • layout_margin・・・上下左右すべて同じ値を指定したい場合
  • layout_marginTop・・・上のみ指定
  • layout_marginBottom・・・下のみ指定
  • layout_marginLeft・・・左のみ指定
  • layout_marginRight・・・右のみ指定
marginの場合、layout_という接頭辞が付きます。個別に設定する機会のほうが多いかと思います。これはCSSのmarginの指定と同じですね。

paddingは以下のようにして設定します。
  • padding・・・上下左右すべて同じ値を指定したい場合
  • paddingTop・・・上のみ指定
  • paddingBottom・・・下のみ指定
  • paddingLeft・・・左のみ指定
  • paddingRight・・・右のみ指定
paddingの場合はpaddingなんちゃらです。これもCSSのpaddingと同じような設定なので、特に違和感ないと思います。

「いやいや!marginとpaddingの概念はわかったけども、そこに指定する値はどうやって算出するのさ!そもそもdpって何よ!?」って思ってる人が多いと思います。
なので、次はAndroidのレイアウトにおける単位を取り扱います。

Chapter08: Viewを重ねる

今回は「Viewを重ねる」です。
Viewを重ねるというのは、画像の上に文字を表示したりする場合などが考えられます。私がよく使っている方法ですが、RelativeLayoutの下にImageViewとTextViewを入れ、margin, paddingをいじって場所決めをして重ねています(margin, paddingについてはChapter09で取り上げます)。

LinearLayoutでは基本的にViewが重なり合うということはないので、RelativeLayoutを使います。


では、サンプルを見てみましょう。
 Sample02アプリを起動して、メニューを押してchapter08を選んでください。
以下のような画面になります。
Chapter07とどこが違うねん!という感じですが、よく見ると、右上にSample02の文字があります。
Sample02という文字列はlayout_alignParentRight=trueが指定されています。これで親要素のRelativeLayoutに対して右寄せの位置に表示されます。しかしバリスタの画像のサイズが ちょっと小さかったので、重なってないように見えたので、ついでだからlayout_margin指定をして画像の上にもってきてみました。

親要素に対して相対的に配置すれば、View同士を重ねることができるわけです。


コードレビュー

ではレイアウトのコードを見てみましょう。 Sample02プロジェクトの/res/layout/chapter08.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">
    <!-- 
        親要素(RelativeLayout)に対して、子要素を相対的に配置します。
        親要素に対して相対的に配置すれば、Viewを重ねることができます。
        今回はTextViewをRelativeLayoutに対して右寄せにして、
        layout_margin***を指定することで、ImageViewの上に重ねました。
     -->
    <ImageView
        android:src="@drawable/barista"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:scaleType="fitCenter"
        android:adjustViewBounds="true" />
    <TextView
        android:text="@string/app_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_marginTop="40dp"
        android:layout_marginRight="40dp" />
</RelativeLayout>
TextViewに、layout_alignParentRight=trueを指定して、親要素のRelativeLayoutに対して右寄せにしています(23行目)。
その後、24行目、25行目でmarginの指定を行っています。親要素のRelativeLayoutからのマージンになっています。重ねるだけならば、そこまで難しいことはありません。複雑なレイアウトも、分解していけばこのレイアウトのような形で、RelativeLayout内で重ねるなどが多いんじゃないかなと思います(あくまで自分の経験の中でですが)

Chapter07: 画像を中央表示する

さて、Sample02プロジェクトを開始します。
まだインポートをしていない方は、「Sample02プロジェクトを公開しました」より、Eclipseへのインポートをお願いします。 今回は、レイアウトの中央に画像を表示する方法です。 まぁこれも、ぴんとくる人はくると思いますが、RelativeLayoutのlayout_centerInParentを使います。 Chapter02で使っている、中央に文字を表示していたやつです。

では、サンプルを見てみましょう。
Sample02アプリを起動してください。
以下のような画面になります。
画像はネスカフェのバリスタです。
Androidで画像を扱う場合は、ImageViewを使います(ゲームなどでは、SurfaceView)。普段はImageViewを使うと思っていて大丈夫です。

コードレビュー

ではレイアウトのコードを見てみましょう。 Sample02プロジェクトの/res/layout/chapter07.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">
    <!-- 
        layout_centerInParent=trueにしているので、
        画像が領域の中央に配置されます。
        ImageViewのscaleTypeをfitCenterにすることで、
        領域に合わせて自動でリサイズされます。
        scaleTypeは色々あるので、時間があれば他の値も試してみましょう。
        また、解像度の大きい画像を使うとメモリを消費しやすくてアプリが
        強制終了しやすくなります。
        なるべく画面の解像度に近いサイズにしましょう。
     -->
    <ImageView 
        android:src="@drawable/barista"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:scaleType="fitCenter"
        android:adjustViewBounds="true"
    />
</RelativeLayout>
RelativeLayoutが親要素です。その子要素のImageViewに、layout_centerInParentを指定しています(20行目)。

21行目で、scaleTypeという指定を行っています。fitCenterにしておくと、大きな画像の場合、表示領域の大きさ(layout_width, layout_height)に自動的に縮小されます。このscaleTypeもまた色々種類があるのですが、ほぼfitCenterしか使わないんじゃないかと思うので説明は省略します。

22行目のadjustViewBoundsは、描画の縦横比を維持するかどうかです。ImageViewのlayout_width, layout_heightの値が画像の縦横比の影響を受けて変化する、という認識なのですが、違うかもしれません(汗。レイアウトを静的に作るときには、trueにしておけばいいと思っています。

 外部リンクになりますが、Android Wiki UIコンポーネント / ImageView を見たら参考になります。

 スマートフォンはメモリが少ないですし、アプリ毎にメモリ割り当て上限が決まっているので、サイズの大きな画像を読み込ませるとOut Of Memoryという例外が発生してアプリが落ちます。なるべく、端末の解像度に近い画像を準備するようにしましょう(マルチ解像度については別のところで取り上げるのでそれまで待っててください)。

2011年10月3日月曜日

Sample02プロジェクトを公開しました

前回までで、Sample01のプロジェクトの内容が終わりました。
次のプロジェクトを準備してあるので、Sample02プロジェクトをダウンロードしてインポートしてください。やり方は、Eclipseでプロジェクトをインポートする(zipのまま)です。ダウンロードするURLの部分だけ、読み替えてください。よろしくお願いします。

Sample02プロジェクトへのリンク(github)

Sample02プロジェクトで取り扱う内容は、以下の通りです。

  • (Chapter07)画像を中央配置する(Chapter07)
  • (Chapter08)Viewを重ねて表示する
  • (Chapter09)margin, paddingについて
  • (Chapter10)Androidでの単位について(dp, sp, pxなど)

Chapter06: フッターを作る方法

今回はフッターを作る方法です。
サイトを作っていても、全画面表示でフッターを一番下に表示する方法など、結構面倒ですが、Androidのレイアウトではそんなに難しいことではありません。方法はまぁ色々とありますが、感のいい人ならひょっとしたら気づいているかもしれません。

今回は、layout_weightを使います。

layout_weightといえば、Chapter03とChapter04で使いましたね。

では、サンプルを見てみましょう。
サンプルアプリでメニューを押して、chapter06を選択してください。
以下のような画面になります。
フッター側に特別な指定を行うのではなく、上のViewにlayout_weight属性をつけます。

コードレビュー

ではレイアウトのコードを見てみましょう。
Sample01プロジェクトの/res/layout/chapter06.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">
    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
        >
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent">
            <TextView
                android:text="@string/chapter06_long_text"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent" />
        </LinearLayout>
    </ScrollView>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        <Button
            android:text="@string/chapter06_button"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>
</LinearLayout>
ScrollViewにlayout_weight=1をつけています。これにより、同じ階層にあるLinearLayoutは子要素が使っている領域分(ここではボタン)のみ使用することになります。こんな感じで、フッターは簡単に実装できます。 ここでのフッターは常に画面に表示されているフッターになりますが、そうでないフッター(リストの一番下にあるフッターなど)は、またそれ用の実装方法があります。 リストのフッターの実装方法は、ListViewのFooterViewを追加するという方法でJavaでの記述が必要になるので、これ以上はここでは言及しません。 トップ画面でフッターをつけてくれと言われたら、こんな方法がある程度に覚えておいてください。

2011年10月2日日曜日

Chapter05: スクロールするViewの配置

今回はスクロールするViewについてです。
LinearLayoutの中には、いくらでも子要素を入れることができますが、画面内におさまらないくらいに子要素を追加すると、画面からはみ出たままで、画面はスクロールすることができません。

そういうときには、ScrollViewを使います。

よく見かけるリスト化されたView(ListView)や、グリッド表示のView(GridView)は、もともとスクロールするための機能を内包しているので、この処理は必要ありません。長いフォームなどの場合や、長い文章を読ませたい場合などに使うとよいでしょう。

画面をスクロールさせるためのViewですが、2種類あります。

  • ScrollView(縦方向のスクロールを担当)
  • HorizontalScrollView(横方向のスクロールを担当)

ScrollViewという名前だと縦横のどちらも使えそうで、縦方向のスクロールViewがVerticalScrollViewとかじゃないのが、何故かという話がちょろっとネットで話題になってたこともありました。ちょっと横道にそれてしまいましたが、HorizontalScrollViewはボタンの配置とかで使うことがあるので、横方向の場合は違う!ということくらいは覚えておきましょう。

では、サンプルを見てみましょう。
サンプルアプリでメニューを押して、chapter05を選択してください。
以下のような画面なります。
ここぞとばかりにTextViewをコピーしまくってみました。下のほうが薄らグラデーションがかかってるのがわかりますでしょうか?続きがあるよというのを見た目的にわかるようにするため、ScrollViewにはそのような機能があります。この範囲の長さなども変更可能です(ここでは取り上げない)

コードレビュー

では、レイアウトのソースコードを見てみましょう。Sample01プロジェクトの/res/layout/chapter05.xmlを開いてください。
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <!-- 
        この例のように、レイアウトに必ずViewが入ってなければいけないわけではありません。
        縦方向のスクロールが欲しい場合は、ScrollViewを使います。
        横方向のスクロールが欲しい場合は、HorizontalScrollViewを使います。
        今回は、ScrollViewのサンプルです。
        ScrollViewは、直近の子要素として、1つのViewしか受け付けません。
        ですので、ScrollViewの下には、LinearLayoutなどを配置し、
        その下にスクロールされるであろう要素を並べます。
        
        また、リスト表示をする場合はListViewなどを使いますが、ListView系は
        すでにScrollViewを梱包しているので、ScrollViewの下にListViewを入れる必要は
        ありません。
        
        あくまで、長いフォームなどを作る場合などに、ScrollViewを使うとよいでしょう。
     -->
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <!-- 手抜きでTextViewをたくさんコピーしています -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/chapter01_linearlayout_vertical" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/chapter01_linearlayout_vertical" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/chapter01_linearlayout_vertical" />
(長いので省略)
    </LinearLayout>
</ScrollView>
ScrollViewの子要素には、1つしか置くことができません。2つ以上の要素を置くとエラーでアプリが落ちます。スクロールバーを表示する対象が1つだけだからなのでしょう。
なので、ScrollViewの子要素にはLinearLayoutもしくはRelativeLayoutなどを配置するとよいでしょう。そのレイアウトの下にはいくらでも子要素を配置できます。

LinearLayoutの中には大量にTextViewを入れています(長いので省略していますが)。
ScrollViewを使えば、後はレイアウトの配置は同じなので、特に困ることはないと思います。しかし、ScrollViewの中にScrollViewを入れたりするとよくわからない感じになることがあるので、そういう使い方はなるべく避けましょう。

最後がアバウトな書き方になってすみません!

2011年10月1日土曜日

Chapter04: 縦方向に均等配置

前回は、横方向に均等配置でした。今回は、縦方向に均等配置です。
内容はどう違うの?という話ですが、ほぼ同じです。
なので、今回はサラッと流します。

注目すべき点は、同じくlayout_weightです。

では、サンプルを見てみましょう。
サンプルアプリで、メニューを押して、chapter04を選択してください。
以下のような画面になります。
この画面は、LinearLayoutで横方向配置を指定した後に、さらにLinearLayoutを3つ入れて、それぞれは縦方向配置の設定をしています。
layout_weightは、レイアウトの重みづけで、高ければ高いほど重要度が増すとChapter03で説明しました。このレイアウトも、Chapter03の縦版なので、同様の設定になっています。


コードレビュー

では、レイアウトのソースコードを見てみましょう。Sample01プロジェクトの/res/layout/chapter04.xmlを開いてください。
<?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">
    <!-- 
        縦方向均等配置をするため、親要素のLinearLayoutのorientationをverticalに設定します。
        ここでは、layout_weightが肝です。指定された方向に対してウェイトが重い分だけ、拡がります。 
        デフォルトは0なので、1つの要素でも1を与えるとそれが残りの領域を占拠します。
        また、均等配置する方向が横の場合、layout_widthに有効な値をいれても無駄になるので0dpを入れます。
     -->
    <!-- 均等配置
         全ての要素にlayout_weight="1"を設定 -->
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent">
        <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="@string/chapter04_button1" />
        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="@string/chapter04_button2" />
        <Button
            android:id="@+id/button3"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="@string/chapter04_button3" />
    </LinearLayout>
    <!-- 1つの要素だけlayout_weightを設定
         ただし、layout_weightが設定されてないボタンが表示されるように、
         layout_height="wrap_content"にしてます。 -->
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent">
        <Button
            android:id="@+id/button4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/chapter04_button4" />
        <Button
            android:id="@+id/button5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/chapter04_button5" />
        <Button
            android:id="@+id/button6"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="@string/chapter04_button6" />
    </LinearLayout>
    <!-- 全ての要素にlayout_weightを設定
         但し、button9のみweightを2にしている -->
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent">
        <Button
            android:id="@+id/button7"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="@string/chapter04_button7" />
        <Button
            android:id="@+id/button8"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="@string/chapter04_button8" />
        <Button
            android:id="@+id/button9"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="2"
            android:text="@string/chapter04_button9" />
    </LinearLayout>
</LinearLayout>
※以降、Chapter03と同じ言い回しになりますので、無視したい人は無視してください。
注目するべきは、layout_heightの値を0dpにしているところですlayout_heightに0dp以外の有効な値を入れると、無駄に解析が入ってしまって非効率だからです。モバイル端末は基本的に非力だしバッテリー持たせる必要があるので、無駄な処理は極力削ります。それはプログラムだけでなく、レイアウトのXMLでも言えるのです。

layout_heightの効能は、LinearLayoutのorientationの方向で意味づけが決まるので、verticalの場合は、縦方向に効きます。なので、layout_height="0dp"なわけです。

均等配置がlayout_weightを指定するだけで簡単にできることがわかってもらえたかと思います。

Chapter03: 横方向に均等配置

LinearLayoutとRelativeLayoutの説明が終わりましたので、ここからはLinearLayoutを使った具体的なレイアウトの方法に入っていきます。とはいってもまだ基本編です。

アプリを使っている人はよく見かけていると思うのですが、ボタンなどの均等配置はどうやるのだろうか?と。CSSならば、もともと決まっているwidthの値を3で割って…などだと思うのですが、Androidでは、もっと簡単な方法が準備されています。

それはlayout_weightという属性です。

では、サンプルを見てみましょう。
サンプルアプリで、メニューを押して、chapter03を選択してください。
以下のような画面になります。
この画面は、LinearLayoutで縦方向配置の指定をした後に、さらにLinearLayoutを3つ入れて、それぞれは横方向配置の設定をしています。

先ほど言った、layout_weightという属性は、レイアウトの重みづけに当たります。高ければ高いほど、重要度が増します。下の画像の赤枠で囲まれている部分のように均等に配置したい場合は、layout_weight全て1にします。

他の場合も見てもらおうと思い、設定してありますが、2行目のボタンの部分は、ボタン4、ボタン5にはlayout_weightを指定していません。そして、ボタン6のみ、1を指定しています。すると、ボタン6が一番重要ということになり、残された領域全部を使います。

3行目のボタンは、全てにlayout_weightを指定していますが、ボタン9のみ、layout_weightに2を指定しています。重要度に比例して領域を確保するので、1:1:2の比率で配置されているのがわかります。



コードレビュー

では、レイアウトのソースコードを見てみましょう。 Sample01プロジェクトの/res/layout/chapter03.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">
    <!-- 
        横方向均等配置をするため、親要素のLinearLayoutのorientationをhorizontalに設定します。
        ここでは、layout_weightが肝です。指定された方向に対してウェイトが重い分だけ、拡がります。 
        デフォルトは0なので、1つの要素でも1を与えるとそれが残りの領域を占拠します。
        また、均等配置する方向が横の場合、layout_widthに有効な値をいれても無駄になるので0dpを入れます。
     -->
    <!-- 均等配置
         全ての要素にlayout_weight="1"を設定 -->
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/button1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/chapter03_button1" />
        <Button
            android:id="@+id/button2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/chapter03_button2" />
        <Button
            android:id="@+id/button3"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/chapter03_button3" />
    </LinearLayout>
    <!-- 1つの要素だけlayout_weightを設定
         ただし、layout_weightが設定されてないボタンが表示されるように、
         layout_width="wrap_content"にしてます。 -->
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/button4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/chapter03_button4" />
        <Button
            android:id="@+id/button5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/chapter03_button5" />
        <Button
            android:id="@+id/button6"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/chapter03_button6" />
    </LinearLayout>
    <!-- 全ての要素にlayout_weightを設定
         但し、button9のみweightを2にしている -->
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/button7"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/chapter03_button7" />
        <Button
            android:id="@+id/button8"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/chapter03_button8" />
        <Button
            android:id="@+id/button9"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:text="@string/chapter03_button9" />
    </LinearLayout>
</LinearLayout>
注目するべきは、layout_widthの値を0dpにしているところです。dpという単位は初めて聞いたと思いますが、これはAndroidで各端末の解像度の違いを吸収するための単位です。基本的にdpを使ってレイアウトをするのですが、単位は他の記事で扱おうと思っているので、ここではこの辺で割愛します。

じゃあなんで注目やねんという話ですが、layout_widthに0dp以外の有効な値を入れると、無駄に解析が入ってしまって非効率だからです。モバイル端末は基本的に非力だしバッテリー持たせる必要があるので、無駄な処理は極力削ります。それはプログラムだけでなく、レイアウトのXMLでも言えるのです。

layout_weightの効能は、LinearLayoutのorientationの方向で意味づけが決まるので、horizontalの場合は、横方向に効きます。なので、layout_width="0dp"なわけです。

均等配置がlayout_weightを指定するだけで簡単にできることがわかってもらえたかと思います。