2019/02/06
新規プロジェクトを作ってみようで、立ち上げると画面にHello world!と表示されるアプリを作りました。
画面にHello world!と表示されるその仕組みについてのお話の続きです。
前回(【Hello world!の仕組み2】 MainActivity.java と activity_main.xmlについて)は、setContentView(R.layout.activity_main)で、画面に、画面いっぱいサイズのFrameLayoutという枠を仕込むところまで説明しました。
sponsored link
もくじ
onCreate()の続き
Activityを立ち上げた時に最初に呼ばれるonCreate()メソッドの中身を引き続き見ていきます。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .add(R.id.container, new PlaceholderFragment()) .commit(); } }
前回はsetContentView(R.layout.activity_main);まで説明したので、その次です。
savedInstanceState
if (savedInstanceState == null)というif文で分岐しています。
見たら分かると思いますが、savedInstanceStateっていうのは、onCreate()メソッドの引数に渡されているBundleインスタンスです。
Bundleインスタンスっていうのは、画面遷移をする際、次の画面(Activity)に渡したいデータを詰め込んでおく箱みたいなものです。
次のActivityに何かデータを渡したい時は、遷移元のActivityで渡したいデータの詰まったBundleインスタンスを作ってから、そのBundleインスタンスを遷移先のActivityに渡すという仕組みになっています。
普通にアプリを立ち上げた時は何も渡されないので、
savedInstanceState == null
が成立しこのif文の中身が実行されることになります。
何か渡された場合の処理は現状では書いていません。このHello world!と表示するだけのActivityに、何かを受け取ってどうこうするような処理は不要だからです。
Fragmentとは?
では、そのif文の中身を見てみましょう。
getSupportFragmentManager() .beginTransaction() .add(R.id.container, new PlaceholderFragment()) .commit();
※見やすいように改行をいれてます。
一見ややこしいこの一文がしているのは、画面にFragmentを配置する為の処理です。
Fragmentっていうのは、レイアウトのパーツ(断片)みたいなものです。複数のFragmentを組み合わせることで一つの画面レイアウトを構成させることが出来ます。
例えばよくあるニュースアプリを想像してください。
このように、ニュースの見出しを並べた部分を一つのFragmentとして、また、ニュースの本文を表示する部分を一つのFragmentとして設計することで、横向き画面ならの二つのFragmentを左右に並べて表示することもできるし、縦向きならトップ画面は見出しFragmentだけを表示して、その見出しをタップすれば、次のActivityで本文Fragmentを画面いっぱいに表示するなんていう作り方が出来ます。
一つのActivityのレイアウトをベタッと一枚で設計するよりも、このように機能的にひとまとめに出来る部分をFragmentとして個別に作っておけば、それを複数のActivityから再利用したり、横からにゅるっとスライドインさせる機能なんかを実装しやすいので、非常に便利です。
とは言え、Hello world!と表示するだけの本アプリにはそんな高度な機能は要りません。
Fragmentを追加する
ここでしているのは、Fragmentを一枚貼り付けているだけです。
注目するべきは以下の一行です(他の部分は決まった形なのでとりあえず形だけ覚えておいたらいいと思います)。
.add(R.id.container, new PlaceholderFragment())
このadd()メソッドは、FragmentTransactionクラスのインスタンスメソッドです。第一引数に渡されたレイアウトに、第二引数に渡されたFragmentを配置するという機能を持っています。
つまりR.id.containerというレイアウトに、new PlaceholderFragment()というFragmentを追加しています。
R.id.containerというのは、言葉で説明すると「containerというIDが付けられたモノ」という意味です。containerというIDが付けられたモノって何か覚えてますでしょうか?activity_main.xmlで定義されているFrameLayoutですね。すなわち、この画面(MainActivity)の大枠であるFrameLayoutです。(参考:【Hello world!の仕組み2】 MainActivity.java と activity_main.xmlについて)
そして、new PlaceholderFragment()というのは見ての通りPlaceholderFragmentクラスをインスタンス化したものです。これがこの画面に貼り付けるFragmentです。つまりPlaceholderFragmentクラスというのはこのFragmentを定義したクラスなわけです。
PlaceholderFragmentクラスはMainActivity.javaの下の方に定義されています。どんなFragmentなのか見てみましょう。
PlaceholderFragment
public static class PlaceholderFragment extends Fragment { public PlaceholderFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_main, container, false); return rootView; } }
PlaceholderFragmentクラスは、Fragmentクラスを継承しています。
Activityクラスを継承したMainActivityクラスがActivityとして扱われるように、Fragmentクラスを継承したPlaceholderFragmentクラスはFragmentとして扱えます。
PlaceholderFragmentクラスで定義されているのはご覧のように、コンストラクタとonCreateView()メソッドの二つだけです。
コンストラクタは見ての通り特に何もしていません(PlaceholderFragmentインスタンスを作るだけ)。
onCreateView()メソッドはこのFragmentの見た目を作る非常に重要なメソッドです。
onCreateView()
このonCreateView()は、ActivityクラスのonCreate()と同じく、システム側から然るべきタイミングで自動的に呼ばれるコールバックメソッドです。
onCreateView()メソッドの戻り値と引数に注目して下さい。
public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState )
※見やすいように改行を入れています。
見ての通り、onCreateView()は引数を3つ受け取っています。そしてViewオブジェクトをリターンします。
1つ目の引数、inflaterは、LayoutInflaterクラスのインスタンスです。これはレイアウトを描画するために必要なインスタンスで、わざわざ自分でインスタンスを作らなくていいようにシステム側から渡してくれているだけです。親切です。
2つ目の引数には、このFragmentを配置する際の親となるViewGroupオブジェクトが渡されます。この場合、このFragmentの親に当たるのはMainActivityの大枠として配置してあるFrameLayoutを指しています。ちなみにFrameLayoutクラスはViewGroupクラスを継承しているので、ViewGroupオブジェクトとして扱えます。
3つ目の引数、savedInstanceStateはMainActivityのonCreate()メソッドに渡されているのと同じBundleインスタンスです。
戻り値としてViewオブジェクトを返すようになっていますが、何を返すのかと言うと、このFragmentの見た目(レイアウト)です。
つまり、onCreateView()メソッドは、このFragmentを表示する際にシステム側から自動的に呼ばれて、そのFragmentの見た目(レイアウト)を戻り値としてシステム側に返すメソッドなわけです。
onCreateView()の中身
では、一体どんなレイアウトを返しているのかメソッドの定義部分を見てみましょう。
View rootView = inflater.inflate(R.layout.fragment_main, container, false); return rootView;
まずリターンするべきViewオブジェクト、rootViewを宣言して、そこに何かややこしいのを代入しています。
このややこしいのは何をしているかと言うと、Fragmentとして表示するべきレイアウトを描画しています。そしてその出来上がったレイアウトを、宣言したrootViewに代入しています。
このinflate()メソッドは、見ての通りのonCreateView()の引数として受け取ったinflater(LayoutInflaterクラスのインスタンス)のインスタンスメソッドです。第一引数はR.layout.fragment_main、第二引数はonCreateView()メソッドの引数として受け取ったcontainer、第三引数はfalseになっています。
ここで重要なのは第一引数のR.layout.fragment_mainです。これはfragment_main.xmlというxmlファイルを指しているのですが、inflate()メソッドというのは第一引数に受け取ったxmlファイルを元にそのレイアウトを描画するメソッドなんです。
第二引数(container)、第三引数(false)は決まった形として覚えていたらいいと思います(解説省略)。
R.layout.○○○というのは、ファイルそのものではなく実はただのint型変数(数字)です。その数字がファイルの存在場所を示しています。
R.layout.fragment_mainがどこを指しているかと言うと、プロジェクトフォルダ(FirstAppli)の中のresフォルダの中のlayoutフォルダの中にあるfragment_main.xmlのことを指しています。
fragment_main.xml
ではこのFragmentのレイアウトはどんな風に定義されているのかfragment_main.xmlを見てみましょう。
fragment_main.xmlを開いて、グラフィカルレイアウトというタブをクリックします。
直接ソースコードをいじるならfragment_main.xmlというタブでできますが、グラフィカルレイアウトというタブを開けば、ソースコードを触らずに直感的な操作と簡単なプロパティ値の入力だけでfragment_main.xmlを編集することが出来ます。
初心者にはグラフィカルレイアウトの方が楽なので、まずはそっちでやりましょう。
グラフィカルレイアウトを開いたら、右にアウトラインという枠が表示されていると思います。
ここには、このxmlファイルの内容のアウトラインが表示されます。見ての通り、fragment_main.xmlのアウトラインは、RelaiveLayoutというのがあって、その下層(内部)にTextViewというがあるのが分かると思います。折り畳んだり開いたり出来ますよね。
RelativeLayout
RelativeLayoutというのは、FrameLayoutと同じくLayoutの一種です。
Layoutというのは、その中にボタンや文字列や画像などのViewを配置するための枠です。ここではRelativeLayoutの中にTextViewという文字列を表示するViewを配置しています。
RelativeLayoutやFrameLayoutなどLayoutには数種類あるのですが、Viewの配置のされ方にそれぞれルールがあります。
配置のされ方のルールというのはどういうことなのか理解できるように、主な3つのレイアウトの特徴を説明します。
- FrameLayout
- FrameLayoutはViewを配置する位置を選ぶことができません。この枠の中にViewを追加すると左上に詰める形で配置されます。複数のViewを追加したら全部のViewが左上に詰める形で配置されるので結果的にViewが重なってしまいます。なのでこのレイアウトは複数のViewを配置するのには向いてません(というか正直、あまり使い道ありません)。
- LinearLayout(Vertical)
- このレイアウトにViewを追加すれば順番に縦に並んでいきます。横に並べることは出来ません。勝手にViewが並んでくれるので配置するのが非常に楽ちんです。
- RelativeLayout
- このレイアウトはViewを自由に配置することができます。しかしその分どこに配置するのかを細かく指示する必要があるのでちょっと大変です。
ではこのRelativeLayoutについて、もう少し詳しく見てみましょう。
アウトラインのRelativeLayoutを選択した状態で、下のプロパティというタブをダブルクリックしてください。
すると、RelativeLayoutのプロパティが画面いっぱいに開きます。
このRelativelayoutのプロパティとして設定されているのは、
Padding Left
Padding Top
Padding Right
Pdding Bottom
の4つです。上の方にPadding Leftがなぜかもう一つありますが、同じものです。
paddingというのは余白のことです。このRelativeLayoutの左、上、右、下それぞれの余白を設定しています。
@dimen/activity_horizontal_marginと@dimen/activity_vertical_marginっていうのは、dimens.xmlというxmlファイルで定義されている値です。dimens.xmlがどこにあるかと言うと、プロジェクトフォルダ(FirstAppli) → resフォルダ → valuesフォルダ内にあるので確認してみて下さい。16pxになってるのが分かると思います。
TextView
このRelativeLayoutの中にTextViewが配置されています。
先ほど、RelativeLayoutは自由にViewを配置することができると言いましたが、デフォルト状態ではやはり左上に配置されます。ここでは親レイアウトに余白が設定されているのでその内側の左上いっぱいいっぱいに配置されています。
では、このTextViewのプロパティを開いてみましょう。
Textの欄の値を見て下さい。やっと辿り着きました。Hello world!です。
この欄に直接、Hello world!と書いてもいいのですが、ここでは先ほどの@dimen/○○○と同じように、@string/○○○という形でxmlファイルにて文字列を定義しています。プロジェクトフォルダ → resフォルダ → valuesフォルダの中のString.xmlを見て下さい。
hello_worldというのはname(変数名みたいなもの)、そのvalue(値)がHello world!です。
@string/[name]で、その[value]を呼べるんですね。
つまり、このstring.xmlのhello_worldのvalueの欄に書いてある文字列が、このアプリの画面に表示されるようになっているわけです。
ここを書き換えてからアプリを立ち上げると表示される文字が変わるのが確認できると思いますので、ぜひやってみて下さい。
Hello world!の仕組み
いやぁしかし、Hello world!がこんなに遠いとは思ってませんでした。
ここまで説明した内容をサクッとまとめましょう。
・トップ画面についてはMainActivity.javaで定義している旨を書く。
MainActivityのonCreate()でしていること
・画面いっぱいの大きさのFrameLayoutをセットする。
・Hello world!という文字列(TextView)が乗ったFragmentを作成して、そのFragmentをFrameLayoutに貼り付ける。
簡単に書けばこれだけのことです。
余分な説明が多かったかも知れませんが、もしこの長ったらしい説明が役に立ったというAndroidアプリ初心者が一人でもいれば嬉しいです。
ってなんか最期の挨拶みたいになってますが(笑)、まだHello world!です。やっとスタート地点です。
これから、このアプリにいろんな機能を追加していこうと思ってます。少しずつ機能を追加しながら説明していけたらいいなと考えております。
ではでは。
今だけ→転職できなければ全額返金の「エンジニア転職保証コース」
絶対エンジニアになる!→テックエキスパート
フリーランスエンジニアの収入例を見てみる→レバテックフリーランス
コメント
[…] 【初心者向け】 ・一番かんたんなJava入門 →仕組み①マニュフェストファイルとは →仕組み③表示される画面の仕組み(Fragment) […]
by Android関連 | 正悟の日常 2020/08/31 18:55