読者です 読者をやめる 読者になる 読者になる
スポンサーリンク

AndroidのUIで,レイアウトXMLの記述を簡素にするための,7つの基礎知識

Android デザイン x個のy Eclipse XML


AndroidアプリのUIは,レイアウトXMLに記述する。

が,これが結構面倒くさい。


各ビューごとに大量の属性をコーディングすることになり,

あとから見返すと,何の目的で属性を付与したのか不明だったりする。

また記述量が多いと,その分だけ変更時の修正作業量も増えてしまう。


どうすれば,AndroidのレイアウトXMLの記述をシンプルに短くできるか?

下記では,基本的なTipsを7つ列挙する。

  • (1)IDE上で自動整形して見やすくする
  • (2)複数のプロパティを,スタイルXML中でまとめて一括指定する
  • (3)共通部分をincludeする
  • (4)独自の性質を持ったViewを作成する
  • (5)アプリ全体のスタイルをテーマに集約する
  • (6)XMLに書かず,コードで動的に操作する
  • (7)HTML5を使う

(1)IDE上で自動整形して見やすくする

まず,本質的でない外見の部分によって,コードの可読性が損なわれている場合がある。

それを解消する。


特に,ビューに付与された複数の属性が,縦方向にインデントがそろっているかどうか?

という点が見やすさを左右する。


この自動フォーマット操作は,Eclipse上のショートカットキーで実行できる。

Androidアプリ開発向け Eclipse XML Formatter
http://andbrowser.com/development/kno...

  • [Window]→[Preference]でPreferenceを開いて、[XML]→[XML Files]→[Editor]

Split multiple attriutes each on a new line
複数の属性をそれぞれ新規行に分割

Align final bracket in multi-line element tags
複数行要素タグに最終括弧を位置合わせ

キーボードショートカットの Shift+Ctrl+f で実行可能。


これで,ゴチャッとしたXMLの内容が,まずは表面的な見かけだけでも綺麗になる。


(2)複数のプロパティを,スタイルXML中でまとめて一括指定する

見かけの整理整頓が終わったら,次は記述量の削減である。


各Viewに対して,

  • 「このViewは,どのような目的で存在するViewなのか」

という,Viewの機能的な存在目的があるはずだ。


そして,その「Viewの機能的役割」さえ分かれば,各種のアトリビュートは後付けである。

ちょうどHTMLの各種タグが文書中で果たすセマンティクスと,CSSによる具体的なデザインとがうまく分離されているように。


同じように,AndroidのレイアウトXML上でも「スタイル」を作る。

CSSのclassのようなもの。

特定のスタイル名を持つViewは,そのスタイルで定義された属性セットを付与される。



下記はサンプルコード。


valuesフォルダ内にstyles.xmlを作成:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- HogeであるようなView用 -->
    <style name="HogeStyle">

        <item name="android:layout_height">fill_parent</item>
        <item name="android:layout_width">0dp</item>

        <item name="android:layout_weight">0.3</item>
        <item name="android:layout_margin">10dp</item>

        <item name="android:src">@drawable/icon</item>

    </style>

    <!-- 下記にstyleタグを増やしていく -->

</resources>


レイアウトXML側で参照:

	<ImageView
		android:id="@+id/img1"
		android:layout_marginLeft="125px"
		
		style="@style/HogeStyle"
	/>

	<ImageView
		android:id="@+id/img2"
		android:layout_marginLeft="100px"
		
		style="@style/HogeStyle"
	/>


こうすれば,

  • レイアウトXML中では,各Viewごとに属性値の異なる部分だけを記述すればよく,コーディングの量が減る
  • レイアウトXML中で,各Viewの持つ役割を把握しやすくなる。(Hogeという目的・役目を持つViewなのだ,とわかる)
  • スタイルXML中での変更が,スタイルを適用している全Viewに反映されるので,デザインをすばやく変更でき,保守性が高くなる。


スタイルは,継承できる。

ベースになるスタイルに,バリエーションを持たせた子スタイルをたくさん作れる。

styleタグにparent属性を指定すればよい。

ThemeとStyle
http://renapp.kakoku.net/menu2_androi...


※なお,id属性はスタイルに含めてはいけない。

レイアウトXML上に直にidを書かないと,Rクラスの中にidを含めてもらえなくなるので,Java側からViewを参照できなくなってしまう。


これで,レイアウトXML内での各Viewに関する記述量が大幅に削減される。


(3)共通部分をincludeする

各Viewに関する記述量が減ったら,次は,Viewそのものに関する記述を簡略化したい。

つまり,個別にViewを指定するのではなく,複数のViewをまとめてグループとして呼び出せるようにする。


このためには,再利用可能なレイアウト部品を作り,それをincludeする。


そうすれば,いちいちView部品を記述せず,

「この機能を持ったレイアウト部品」というふうに部分的なUIを呼び出して組み合わせられるので,

UIの実装効率も,XMLの可読性も,デザイン全体の修正効率も向上する。


サンプルコード:

メインのレイアウトXML側で

        <include layout="@layout/_fuga" />


_fuga.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android" >

    <RelativeLayout style="@style/hoge" >

        <ImageView
            android:id="@+id/iv1"
            style="@style/HogeImage" />

        <ImageView
            android:id="@+id/iv2"
            style="@style/HogeImage" />

    </RelativeLayout>

</merge>

こうすれば,レイアウト部品がメインのXML中に埋め込まれて置換されたような状態になる。


なお,レイアウト部品側のルートViewをmergeタグにしている理由は,

includeする側のXMLから見て透過的に部品を埋め込むため。


もし普通のLinearLayoutなんかをルートにした場合,

Javaコード側から部品レイアウト内へのアクセスがちょっと面倒になるので,それは避ける。

Android開発のちょっとしたお話
http://alpha.mixi.co.jp/2011/10805/

  • XMLで画面を作っていると、あるパーツは他の画面でも使いたいので、その部分だけ切り出したい
  • そのような場合には、切り出したい部分だけを記述したXMLを用意し、使いたい場所でインクルードする


AndroidのレイアウトXMLで別のレイアウトを再利用してみる
http://devslog.com/article/2011120723...

  • IncludeしたViewを操作したい時:findViewByIdでレイアウトを取得し、そのレイアウトからからビューを取得


Androidトレーニング <include/>でレイアウトを再利用する
http://firespeed.org/diary.php?diary=...

  • <include/>タグを使用して)他のレイアウトにこのレイアウトを含めるとき、システムはmergeエレメントを無視し、2つのボタンを統合して直接<include/>タグの代わりに置きます。


これで,個別のViewに対しても,また複数のViewのまとまりに対しても,

共通部分をまとめる事で記述をシンプルにできるようになった。


(4)独自の性質を持ったViewを作成する

ある場合,たくさんのビューを組み合わせても実現が困難,

というUI部品を作りたいときもある。


そういう複雑なViewを実現したい場合は,既存のViewを拡張して,

独自のビュークラスを作る。


独自ビューを作れば,動的な性質を持つ複雑なViewを,共通化してシンプルに記述することができる。

  • 独自Viewのコンストラクタ内で各種プロパティをsetすれば,XML内にViewの属性をセットしなくて済む。
  • 独自Viewクラスを作れば,onDraw()メソッド内でcanvasオブジェクトを受け取り,ビュー上に任意の描画操作を実行できる。
  • 各種イベントリスナも独自クラス内で共通化して定義できるので,個別のViewインスタンスごとにイベントリスナを設定する手間が無くなる。


ViewとかButtonとかをextendしたクラスを作成。

独特のコンストラクタを作る必要があるので注意。


HogeView.java

public class HogeView extends View
{
    public HogeView(Context context, AttributeSet attr){
        super(context, attr);
    }
}

もし,このメソッドがないとエラーになったりする。親クラスのコンストラクタの仕様に気を配ること。

ERROR/AndroidRuntime(8164): Caused by: java.lang.NoSuchMethodException: HogeView(Context,AttributeSet)
ERROR/AndroidRuntime(8164):     at java.lang.Class.getMatchingConstructor(Class.java:643)
ERROR/AndroidRuntime(8164):     at java.lang.Class.getConstructor(Class.java:472)
ERROR/AndroidRuntime(8164):     at android.view.LayoutInflater.createView(LayoutInflater.java:480)
ERROR/AndroidRuntime(8164):     ... 21 more

カスタムビューの実装
http://d.hatena.ne.jp/Korsakov/201008...

  • 継承したクラスでは、親クラスのコンストラクタをすべて記述しておかないと、何故かエラーが発生するので注意


レイアウトXML側では,パッケージ名を含めて指定。

	<com.example.HogeView
		android:id="@+id/hoge1"
		android:layout_width="300px"
		android:layout_height="300px"
	/>

これで表示できる。

Hoge.javaの中に,Clickイベントに関する処理内容や,キャンバスの描画処理などを含めておけば,

このビューが現れる箇所では共通して同じUIの挙動を実現できる。



具体例としては,下記のエントリで,

Bitmapの表示内容をピクセルごとに制御するような独自Viewを動作させているので,そちらを参照のこと。

Androidで,「ビットマップのピクセル操作」をリアルタイムに実行するサンプルコード
http://language-and-engineering.hatenablog.jp/entry/20120626/AndroidManipulat...

(5)アプリ全体のスタイルをテーマに集約する

テーマを使うと,アプリ内の全体を対象としてデザインのカスタマイズが可能。

  • 基本的な文字色等を統一する
  • プログレスバーやテキスト選択アイコンなど,レイアウトXML中に明示的に現れないUI部分のデザインをカスタマイズする
  • アプリ内の全EditTextのスタイルを一括して変更する

など。

エディットテキストのデザインを変える
http://books.google.co.jp/books?id=jh...

  • style中でThemeを継承し,android:editTextStyleを定義


テーマを使ってレイアウトを定義する
http://techbooster.jpn.org/andriod/ui...

  • マニフェストXMLのapplicationタグにテーマを指定している例


Androidアプリ全体で文字色や背景色を統一させる方法(Theme/テーマ)
http://android.roof-balcony.com/resou...


システムテーマで統一感を出す
http://techbooster.jpn.org/andriod/ui...

ただし,この方法は情報源が少ない。

基本となる組み込み済みのテーマを指定して,ちょっと加工したら,

あとはstyleで個別対応ということになるかもしれない。


(6)XMLに書かず,コードで動的に操作する

Viewのインスタンスを生成してsetContentView()してしまえば,XMLなしでビューを動的に生成できる。

この場合,XMLの記述量はゼロになる。


その分だけ,Javaコード側のコーディングは増えるのだが,

それを楽にコーディングできるようにしたのが,Android-MVCフレームワークのV層である。

Androidアプリの画面レイアウトを,まるでjQueryのようなコードで動的構築できるライブラリ (の試作品。UIコーディングのためのDSL)
http://language-and-engineering.hatenablog.jp/entry/20120210/p1

上記のエントリの,詳細設計の要点は下記の通り。

  • TOPのレイアウトから,内部に含まれるレイアウトまで,再帰的にinflate処理を行なっている。
  • レイアウトにビューを追加する際には,parentLayout.addView(v, new LinearLayout.LayoutParams(intWidth, intHeight)); のように,高さと幅の情報を込みで渡す必要がある。
  • JavaコードがUIの構造をわかりやすく表現している。

これなら,確かにレイアウトXMLの記述量は減る。

(7)HTML5を使う

最後に,究極の手段。

  • レイアウトXMLには,WebViewのみを設置する。
  • WebView内にはHTML(5)を表示する。
  • addJavascriptInterfaceで,HTML側とJava側のロジックを結びつける。

たった2ファイルで,HTML+JS製のネイティブAndroidアプリを作る手順 (動作するサンプルコード付き。WebViewの活用方法)
http://language-and-engineering.hatenablog.jp/entry/20120710/CreateAndroidApp...


AndroidやiOSの「ハイブリッドアプリ」で,JavaScriptとネイティブ・コードが連携する仕組みを図解 (おまけ:HTML側で施すべき,クロスプラットフォーム対策)
http://language-and-engineering.hatenablog.jp/entry/20120713/p1


jQuery Mobile と HTML5 で、Androidのネイティブアプリを作成する手順
http://language-and-engineering.hatenablog.jp/entry/20120717/CreateAndroidNat...

これで,レイアウトXMLには実質的に何も記述せず,別の使い慣れたHTMLファイル上にUIを記述してゆける。



以上,7つの手段を使って,レイアウトXMLに記述するコードの量を極力減らし,シンプルなUI実装を実現できる。

style・includeと独自Viewぐらいは,自由に使いこなせるようになっておきたい。


その他


不要なレイアウト部品を検出して警告するツール:

Android Tips #5 layoutoptでレイアウトを最適化
http://dev.classmethod.jp/smartphone/...

補足

本エントリでは,後方互換性を考慮して,fragmentに関する記述は省略した。

ここで述べた7つの方法は,SDKバージョンを問わずに使える,基本的なテクニックばかり。



関連する記事:

Androidアプリで,レイアウト用XMLの名前をいちいち指定せずに,自動的に画面を描画させよう (Rails風のCoCなレンダリング)
http://language-and-engineering.hatenablog.jp/entry/20110910/p1


Androidアプリで,_("リソース名") と書くだけで,簡単に文字列を参照しよう
http://language-and-engineering.hatenablog.jp/entry/20110815/p1


Androidで,複数のAnimationを「順番に」実行するためのライブラリ (XMLを使わずに「連続した動きの変化」を指定し,逐次実行するDSL)
http://language-and-engineering.hatenablog.jp/entry/20120416/AndroidAnimation...