SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Android Studio 2で始めるアプリ開発入門

Androidアプリのフラグメント(2) ~一つのAndroidアプリでタブレットとスマホに対応~

Android Studio 2で始めるアプリ開発入門 第10回


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

10インチ用処理

 10インチタブレット用の画面構成がひととおりできた状態で処理を記述していきます。

処理の大きな流れ

 今のままでは、リストをタップしたら以下のように注文完了画面が起動してしまいます。

図6 10インチ画面いっぱいの注文完了画面
図6 10インチ画面いっぱいの注文完了画面

 これでは、まったく意味がありません。そこで、右側の余白に注文完了が表示されるようにしていきます。スマホサイズとの処理の違いを図解すると以下のようになります。

図7 スマホサイズでの動き
図7 スマホサイズでの動き

 まず、スマホサイズではひとつのアクティビティにひとつのフラグメントがのっています。リストアクティビティにはリストのフラグメントがのり、注文完了アクティビティには注文完了フラグメントがのっています。また、リストをタップすると、注文完了アクティビティが起動し、したがって、注文完了フラグメントが動き出します。データの受け渡しもインテントを使用します。

 ところが、10インチになると一画面となり、以下のような動きになります。

図8 10インチタブレットでの動き
図8 10インチタブレットでの動き

 初期表示では図4のように左側にリストが表示されているだけで、右側は空欄です。リストをタップしたら、この空欄であるFrameLayoutに注文完了フラグメントを追加するのです。「リストに戻る」ボタンをタップしたら、追加されたフラグメントを削除します。

 このようにすることで、リストをタップするたびに右側に注文完了メッセージが表示されたような動作になります。

 こういったフラグメントの追加削除を行う先として、画面部品を上に上に重ねて表示できるFrameLayoutは最適なのです。

画面判定

 さて、大きな処理の流れがわかったところで、ソースコードを記述していきます。

 まず、大前提として、フラグメント内で、画面サイズに応じて分岐が生じるということです。例えば、リストフラグメントでは、タップされた際に通常サイズ画面の場合は注文完了アクティビティを起動する必要がありますが、10インチタブレットの場合はフラグメントを追加する必要があります。注文完了画面も同様に分岐が生じます。

 そのため、分岐の基準となる画面サイズ判定フラグを用意することにします。

 MenuListFragmentに、以下のようにフィールドとメソッドを追加してください。

リスト2 MenuListFragmentに判定フラグの追加
public class MenuListFragment extends Fragment {
  private boolean _isLayoutXLarge = true;  // (1)

  @Override
  public void onActivityCreated(Bundle savedInstanceState) {  // (2)
    super.onActivityCreated(savedInstanceState);
    View menuThanksFrame = _parentActivity.findViewById(R.id.menuThanksFrame);  // (3)
    if(menuThanksFrame == null) {  // (4)
      _isLayoutXLarge = false;  // (5)
    }
  }

      ~省略~
}

 10インチタブレットかそれ以外かを判定するフラグフィールドとして_isLayoutXLargeを用意します(リスト2の(1))。初期値としてtrueとしておきます。つまり、初期値は10インチタブレットです。

 そして、実際どのサイズなのかを判定する処理をonActivityCreated()メソッド内に記述します(リスト2の(2))。このメソッドは前回のフラグメントのライフサイクルの図でも出てきましたが、フラグメントが生成される一番最後に実行されるメソッドで、メソッド名の通り、アクティビティが生成されたのちに実行されます。したがって、アクティビティの状態に依存する処理はここに記述します。

 今回、アクティビティ画面上にidがmenuThanksFrameの画面部品、つまり、フラグメント追加用のFrameLayoutが存在するかどうかで、10インチかどうか判定しますので、このメソッドにその処理を記述しています。

 まず、_parentActivityのfindViewById()でmenuThanksFrameの画面部品を取得します(リスト2の(3))。そして、これがnullだったら(リスト2の(4))通常画面サイズと判定し、フラグをfalseにします(リスト2の(5))。

リストタップ時処理

 画面サイズ判定ができたところで、このフラグを使ってリストタップ時の処理を記述します。これは、MenuListFragment内のリスナクラスListItemClickListenerのonItemClick()メソッド内の改造です。以下のようにソースコードを記述してください。

リスト3 onItemClick()内にソースコードを追加
public class MenuListFragment extends Fragment {
      ~省略~
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            Map<String, String> item = (Map<String, String>) parent.getItemAtPosition(position);  // (1)
            String menuName = item.get("name");  // (1)
            String menuPrice = item.get("price");  // (1)

            if(_isLayoutXLarge) {  // (2)
                Bundle bundle = new Bundle();  // (3)
                bundle.putString("menuName", menuName);  // (4)
                bundle.putString("menuPrice", menuPrice);  // (4)

                FragmentManager manager = getFragmentManager();  // (5)
                FragmentTransaction transaction = manager.beginTransaction();  // (6)
                MenuThanksFragment menuThanksFragment = new MenuThanksFragment();  // (7)
                menuThanksFragment.setArguments(bundle);  // (8)
                transaction.replace(R.id.menuThanksFrame, menuThanksFragment);  // (9)
                transaction.commit();  // (10)
            }
            else {  // (11)
                Intent intent = new Intent(_parentActivity, MenuThanksActivity.class);  // (12)
                intent.putExtra("menuName", menuName);  // (12)
                intent.putExtra("menuPrice", menuPrice);  // (12)
                startActivity(intent);  // (12)
            }
        }
    }
}

 (1)(12)はもともと記述されていたソースコードです。

 ソースコードが記述できた時点で、一度アプリを再起動してみてください。リストをタップすると、右側に注文完了フラグメントが表示されるはずです。ただし、MenuThanksFragmentの方はまだ改造していませんので、注文したメニュー名や値段は表示されませんし、「リストに戻る」ボタンをタップするとアプリ自体が終了してしまいうので注意してください。

フラグメントの追加

 では、追記したソースコードの解説をしていきます。_isLayoutXLargeフラグを使って分岐を行っているのが(2)です。_isLayoutXLargeがfalse、つまり通常サイズの画面の場合は、前回記述した処理となるので、もともとあった(12)のソースがelse以下に移動しています(リスト3の(11))。ここでは、_isLayoutXLargeがtrue、つまり、10インチ画面の場合である(3)(10)の記述に注目します。これが、menuThanksFrameのFrameLayoutにフラグメントが追加される処理です。

 アクティビティへのフラグメントの追加処理は以下の手順です。

 1) フラグメントの管理を行うクラスFragmentManagerオブジェクトを取得します。これは、getFragmentManager()メソッドを使用します(リスト3の(5))。

 2) FragmentManagerからFragmentTransactionオブジェクトを取得します。フラグメントの追加削除は、あたかもデータベースへのデータの追加削除のように、トランザクションという考え方を採用しています。これを制御するクラスがFragmentTransactionクラスです。このオブジェクトは、FragmentManagerのbeginTransaction()メソッドで取得します(リスト3の(6))。

 3) 追加するFragmentオブジェクトを生成、もしくは取得します(リスト3の(7))。この場合は新規追加なので、MenuThanksFragmentクラスをnewしています。

 4) フラグメントの追加・削除・置き換え処理を行います(リスト3の(9))。これらは、FragmentTransactionクラスのメソッドを使います。追加する場合はadd()、削除する場合はremove()です。この削除と追加を同時に行う処理、つまり置き換え処理がreplace()です。add()もreplace()も引数を2個必要とし、第1引数は追加先のレイアウト画面部品です(これを「コンテナ」といいます)。第2引数は追加するFragmentオブジェクトです。removeの引数は、削除するFragmentオブジェクトのみです。

 5) コミットします(リスト3の(10))。これは、FragmentTransactionクラスのメソッドcommit()を使います。手順2)で説明したとおり、フラグメント処理はトランザクションという考え方を採用していますので、このcommit()メソッドが実行されるまで、フラグメントの状態は反映されません。

 ところで、これから追加するフラグメントにデータを渡したい場合があります。今回では、メニュー名と金額を渡す必要があります。その処理がリスト3の(3)(4)(8)です。

 Androidではこういう引継ぎデータを管理するクラスとして、Bundleクラスというのが用意されています。実は、インテントを使用してアクティビティ間のデータ受け渡しを行う際、Intentの内部ではこのBundleクラスが使われています。そのBundleクラスをnewします(リスト3の(3))。生成したBundleオブジェクトにデータを格納します(リスト3の(4))。そして、生成したFragmentオブジェクトのsetArguments()メソッドを使用して、このBundleオブジェクトを渡します(リスト3の(8))。これで、追加するフラグメントにデータを渡すことができます。

 なお、Intentの内部でもこのBundleオブジェクトが使われていることから、リスト3のソースコードは、Bundleオブジェクトへのデータ格納を共通化し、if-else内を以下のようにもう少しシンプルに記述することができます。

リスト4 Bundleオブジェクトへのデータ格納を共通化したonItemClick()
Bundle bundle = new Bundle();  // (1)
      ~省略~

if(_isLayoutXLarge) {
      ~省略~
    menuThanksFragment.setArguments(bundle);  // (2)
      ~省略~
}
else {
    Intent intent = new Intent(_parentActivity, MenuThanksActivity.class);
    intent.putExtras(bundle);  // (3)
    startActivity(intent);
}

 (1)のように、Bundleオブジェクトへのデータ格納をif-elseの外側で行います。(2)はリスト3と同じです。違いは(3)です。リスト3ではputExtra()メソッドを使って各々データを格納していましたが、IntentクラスのputExtras()メソッドを使用すれば、Bundleオブジェクトを直接渡すことができます。

 ダウンロードサンプルはこのように記述していますので、参考にしてください。

次のページ
MenuThanksFragmentの改造

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Android Studio 2で始めるアプリ開発入門連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 齊藤 新三(サイトウ シンゾウ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook<個人紹介>WINGSプロジェクト所属のテクニカルライター。Web系製作会社のシステム部門、SI会社を経てフリーランスとして独立。屋号はSarva(サルヴァ)。HAL大阪の非常勤講師を兼務。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/9639 2018/05/23 15:36

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング