CodeIQ MAGAZINECodeIQ MAGAZINE

Android初心者におすすめする「Fragmentによるシンプルで効率的なUI実装」#Android #yahoo

2014.03.28 Category:技術コラム Tag: , ,

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

Android 3.0で導入され、Android 4.0からスマートフォンで利用できるようになった注目機能である「Fragment(フラグメント)」。

今回はヤフー矢端智光さんに、Fragmentでシンプルに実装できる効率的なUI設計についてレポートを書いていただきました。
by 馬場美由紀 (CodeIQ中の人)

Fragmentでシンプルで効率的なUIの開発を

ヤフー矢端です。

FragmentはAndroid3系から使用可能な機能なので、知っている人にとっては今さらという内容になるかもしれません。

しかし日本の市場では2.x系のAndroid端末を保有するユーザーの方も少なくなく、アプリを2.x系以上で対応していた都合から、「Fragmentを知っているけど、まだあまり触ったことがない」という開発者の方もいるのではないかと思います。

そこで本稿では、開発で新たにFragmentを導入する方や、Android初心者の方がFragmentの導入を学んだ後にできる、画面切り替えをFragmentを利用してシンプルに実現していく方法を紹介していきたいと思います。

本稿では、「support-library-v4」を利用したFragmentの実装方法でコードを記載しております。support-libraryについてはAndroid Developerをご覧ください。

導入編

導入の方法についてはいろいろな技術サイトで説明されているので、ここではさくっと説明させていただきます。

Activityに追加する

FragmentActivityというクラスを継承をして実装していきます。

FragmentManager#beginTransaction() メソッドでトランザクションを取得して、Fragmentのインスタンスを追加など変更を加えていき、反映する場合は最後に必ずcommitを呼びます。

FragmentManager manager = getSupportFragmentManager();
TestFragment testFragment = new TestFragment();

FragmentTransaction transaction = manager.beginTransaction();
transaction.add( R.id.test_layout, testFragment, "test" );
transaction.commit();

これでFragmentがActivityに追加され、Fragmentのライフサイクルが機能し始めます。

実装編

Fragmentを利用して、アプリを実装していくにあたって、複数のFragmentを一つのActivity上で扱っていくことになる場合、Activity上で無駄なく管理ができるように実装していくことが必要になっていきます。

Fragmentの階層は浅くを意識する

直接話題には関係ありませんが、何も意識せずにFragmentにFragmentの追加を重ねていき、階層が段々深くなっていくと

  • ActivityからのFragmentへの処理の通知をする場合、実装が複雑になる。
  • レイアウトの階層が深くなる。

といったことが、実装中に気づかぬところで発生してしまうかもしれません。

レイアウトの階層が深くなるとStackOverFlowErrorが発生します。Android4系以上では発生しにくくなりますが、深いレイアウトはあまりよいとはいえません。Fragment自体の階層を減らすか、mergeタグを活用することで改善することができます。

そこで最初に、ActivityからFragment。FragmentからFragmentへの、親から子へのイベントの通知をシンプルに実装する方法について紹介していきます。

実装編:基底クラスを用意する

アプリで使うFragmentの基底クラスを用意して、その基底クラスを継承するようにするだけで、Fragmentを扱うクラスをより効率的に扱うことができます。

例えばActivity上に追加した全Fragmentに対して、表示の更新処理を通知したい場合、よくあるのはFragmentManagerからfindFragmentByIdなどで追加済みのFragmentのインスタンスを取得して、それぞれのインスタンスのメソッドを呼んであげなければなりません。

FragmentManager manager = getSupportFragmentmanager();

//transactionで追加したときに指定したタグかviewのIDから取得をする方法
Test1Fragment test1 = (Test1Fragment) manager.findFragmentByTag( "tag" );
Test2Fragment test2 = (Test2Fragment) manager.findFragmentByTag( R.id.viewID );
//各フラグメントを更新
test1.reload();
test2.reload();

そこで基底クラスを用意してあげます。

基底のクラスを用意

public abstract class BaseFragment extends Fragment {

    public void reload() {}
}

FragmentManagerには、現在保持しているFragmentをListで返す getFragments() というメソッドがあります。上記で行ったイベントの通知が、下のように実装できます。

FragmentManager manager = getSupportFragmentmanager();
List<Fragment> list = manager.getFragments();

for ( Fragment fragment : list ) {

    ( (BaseFragment) fragment ).reload();
}

この例だと行数はほとんど変わりませんが、これがActivity上のFragmentがさらに増えていった場合、その差がはっきり現れます。あとは基底クラスを継承したクラスでメソッドをオーバーライドして処理を実装するだけです。

共通の基底クラスを用意しておくと、ほかにも一つのレイアウト上で複数のFragmentを切り替えている場合に、そのFragmentのインスタンスのチェックや、クラスのキャストをする手間を省いて、基底クラスのメソッドを呼び出すだけになるので、ActivityからのFragmentへのイベント通知の実装がシンプルになります。

実装編:FragmentTransactionを使いこなす

FragmentManagerから追加・削除など変更を行う場合に必要なFragmentTransactionですが、この機能を理解すると、FragmentTabHostやFragmentPagerAdapterと同じようなことが独自に実装できるようになります。

今回はFragmentTabHost のような 機能を実装しながらtransactionについて説明していきます。

FragmentTabHostとは

tabhost

タブをタップすることでFragmentを切り替えられるようになります。実装の仕方は調べていただければわかると思います。

簡単に実装できる分、多少制約があったりもします。

FragmentTabHostのような機能を実装してみる

制御をするためのクラスを一つ作ります。コンストラクタや入力チェックなどは省略してあります。

省略した部分では以下のようなメソッドが実装されています。

  • コンストラクタ:FragmentManagerのインスタンスと表示するViewのIDをメンバに渡す
  • HashMapにFragmentとそれに関連付けるキー値を渡してメンバで保持する。
    //キーに関連付けたFragmentの表示する
    public void change( final String aKey ) {
        final Fragment currentFragment = _mManager.findFragmentById( _mId );
        final FragmentTransaction transaction = _mManager.beginTransaction();

        if ( currentFragment == null ) {
            //初回は指定したviewIDにFragmentを追加する
            for ( Map.Entry<String, Fragment> entry : _mMap.entrySet() ) {
                transaction.add( _mId, entry.getValue() );
            }
        }
        //表示状態を切り替える
        for ( Map.Entry<String, Fragment> entry : _mMap.entrySet() ) {

            final Fragment fragment = entry.getValue();
            if ( aKey.equals( entry.getKey() ) ) {
                //fragmentを表示状態に変更する
                transaction.show( fragment );
            } else {
                //fragmentを非表示状態にする
                transaction.hide( fragment );
            }
        }
        //変更の反映
        transaction.commitAllowingStateLoss();
    }

あとは、表示したいFragmentのインスタンスを追加し、ボタンなどのイベントと関連したページのキーを渡すようにするだけで以下のように表示を切り替えることができます。

表示しているFragmentは、page1から順に アナログ時計・webView・カレンダーをレイアウトに配置しただけのFragmentになります。

FragmentTransactionで加えられる変更とその挙動

上記で、FragmentTabHostのような機能を実装しました。画面を切り替える際に行っていたことは

  • FragmentManager#beginTransaction()でトランザクションを開始
  • 初回実行時は指定されたviewIDにFragmentを追加する。
  • キーで指定されたフラグメントを表示状態に変更。
  • キーで指定されたフラグメント以外を非表示状態に変更。
  • commitで変更を反映。

になります。

上記で行った変更と、ほかによく使うわれるであろうremoveとreplaceの計5つの挙動について紹介します。

  • add:(ViewID, Fragment, TAG)
    ViewIDまたはタグと関連付けてFragmentを追加する
  • remove(Fragment)
    Fragmentを削除する。
  • replace(ViewID, Fragment, TAG)
    ViewIDまたはタグと関連付けてFragmentを追加する。
    addと違う点は、指定したViewIDまたはタグにすでにFragmentが存在する場合は、そのIDまたはタグに属するFragmentをすべてremoveしてからaddするといった挙動になる。
  • show(Fragment)
    存在するfragmentを表示状態に変更する。Fragmentのライフサイクルは変化しない。
  • hide(Fragment)
    存在するfragmentを非表示状態に変更する。Fragmentのライフサイクルは変化しない。

環境(端末リソース)にやさしい画面変更を実現していく

FragmentTransactionでは上記のような変更のほかに、addToBackStackというメソッドで現在の状態を保存して、バックキーが押されるか、FragmentManager#popBackStackというメソッドを呼び出す等で、元の状態を復元させるといったことも可能です。

FragmentTransaction transaction = manager.beginTransaction();
transaction.add( R.id.test_layout, testFragment, "test" );
//バックスタックに変更前の状態を追加する
transaction.addToBackStack( "first" );
transaction.commit();

これらのTransactionの仕組みを利用して

  • 上記の例のように2~3つの要素を切り替えるだけであれば今回のように showhide で表示を切り替える。
  • 毎回生成したFragmentのインスタンスを追加・削除して表示を切り替えるのであれば、前回表示のFragmentを削除して置き換えてくれる replace で変更をする。
  • バックキーが押されたら、以前表示していた状態に戻す。というのであればTransaction毎にaddToBackStackで状態を保存して戻ってこれるようにする。

といったような表示切り替えが簡単に実装できます。使えるメモリ量の限られたアプリを意識しつつ、実現したいUI/UXに合わせて無駄のないFragmentTransactionの実装をしていきましょう。

最後に

今回は、シンプルな実装でFragmentをレイアウトに適応する場面を主に説明をさせていただきました。これからのAndroidアプリ開発においてFragmentはますます必要不可欠なものになっていくと思います。

今回紹介させていただいたものはほんの一部にすぎませんが、少しでもFragmentを扱う開発の手助けになれば幸いです。

寄稿者プロフィール 矢端 智光(やばた ともみつ)氏
ヤフー株式会社に勤務。ニュースサービスでAndroidアプリの開発に従事。現在、ニュースアプリ「Yahoo!ニュース」の開発を行う。

iOS6から導入されたAuto Layout問題に挑戦してみない?

ヤフー山口恭兵さんからのiOSの問題が出題中です。AndroidもiOSもOK!というエンジニアの皆さん、ぜひ挑戦してみてくださいね。

Autolayoutを設定して画面のサイズ・向きの変化に対応できるレイアウトを作成する問題です。

  • 締切4月14日(月)AM10:00まで
  • 問題挑戦はこちらから

CodeIQコード銀行にあなたのコードを預けてみませんか?

  • CodeIQコード銀行ではあなたのコードを財産と考えます。
  • お預かりいただいたコードは、CodeIQコード銀行がしっかり評価し、フィードバックいたします。
  • 当コード銀行にお預けいただいたコードは、企業がみてスカウトをかける可能性があります。
  • 転職したい方や将来転職することを考えている方で、今の自分のスキルレベルを知りたい方はぜひ挑戦してみてください。
  • 企業からスカウトがきたら困る人は挑戦しないでください。

興味を持った方はこちらからチャレンジを!

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

■関連記事

フィボナッチ数列で学ぶ「Swift」の関数型プログラミング... フィボナッチ数列を題材に、Swiftと関数型プログラミングを学ぼう どうも、@taketo1024こと佐野です。 僕はヤフーで iOS アプリ開発をしており、今年から「プログラマのための数学勉強会」を隔月で開催しております。 7月24日には 第4回の開催を予定しており、これまでCodeIQさん...
佐々木俊尚氏を顧問に、オウンドメディアも開設!家事シェアサービス「Any+Times」は、個人のスキ... 誰かのニーズと誰かのスキルを、Webとリアルで交換 仕事で毎日遅くなるので、掃除や洗濯をする時間が取れない。そんな悩みを持つエンジニアも少なくないのではないだろうか。ハウスクリーニングサービスはいくつもあるが、安心して頼めて、かつ低コストのサービスはそうそうあるものではない。 「私自身が、便利屋...
Monacaでenchant.jsで作ったHTML5ゲームを、シュン君とスマートフォンに移植してみよ... なぜ、スマートフォンアプリ化する必要があるの? 前回までの講師は、UEI清水亮氏(前編・後編)、日本マイクロソフト物江修氏(Windowsストアアプリ編)、Mozilla Japanの清水智公氏(Firefox OS編)。 そして次なるターゲットは、スマートフォンアプリ移植。講師には、アシアル株...
Androidアプリ開発にも役立つEclipseプラグイン活用法 #yahoo #android... Androidアプリにも便利なEclipseプラグイン開発とは ヤフー松田です。 Eclipseは数多くのプラグインで構成されている統合開発環境であり、Androidアプリの開発者にとっても馴染みのあるものと思います。各々のプラグインはEclipseで開発することができるようになっており、さらに...
企画とエンジニアが知っておかないといけない「iBeacon」の話 #iBeacon #yahoo #... ちゃんと理解してますか?Appleの新技術「iBeacon」 ヤフー羽田です。 登場から時間も経ち、サービス化されたり、アプリ化されることも珍しくなくなったiBeacon。 そんな今だからこそiOSに携わる企画者・エンジニアとして「知っておかなければいけないこと」が多々あります。 今回は基礎...
DeNA中の人が解説する「マンガボックス」システム構成とiOSアプリの内部構成 #DeNA #iOS... マンガボックスとは マンガボックスは、2013年12月よりiOSとAndroid向けのアプリが公開され、2014年3月には300万ダウンロードを達成しました。プロの作家による30作品以上のマンガが連載されており、毎日5~6作品づつ更新され続けています。 過去12週間分の作品はすべて無料で読むこと...

今週のPickUPレポート

新着記事

週間ランキング

CodeIQとは

CodeIQ(コードアイキュー)とは、自分の実力を知りたいITエンジニア向けの、実務スキル評価サービスです。

CodeIQご利用にあたって
関連サイト
codeiq

リクルートグループサイトへ