Androidのお勉強 第三回 フラグメントの使用
第三回はフラグメントの使い方を紹介します。
※編集中です
- 第一回 環境構築、AndroidPJの解説、サンプルの作成、イベントの実装、画面遷移(Intent)
- 第二回 ListViewと独自Adapterについて
- 第三回 フラグメントの使用
- 第四回 復元処理の重要性について
- 第五回 非同期通信の重要性、ANRと戦う
- 第六回 独自レイアウトを作ろう。Viewの拡張の注意点。OnMeasureやViewのライフサイクルについて学ぶ
- 第七回 API x SQLiteDB連動アプリを作成してみよう
フラグメントって??
FragmentはAndroid 3.x系から追加されたコンポーネントです。
従来は画面を作成するために、「ViewとActivity」を画面ごとに作成してきました。
似たような画面が複数必要な場合であっても、「ViewとロジックがセットになったActivity」を再度作らなければいけません。
つまり、「画面の再利用」ができなかったわけです。
※layoutでincludeする手法は**「Viewの再利用しかできません」。「ロジックを含んだViewの再利用」**ができなかったって意味です。
そんな中現れたのがFragmentです。
Fragmentが「「ロジックを含んだViewの再利用」」を可能にします。
- FragmentはActivityにレイアウトの一部(layout.xml)に部品として組み込むことができます。
- javaのソース(Activity)内で、動的に追加、削除ができます。
そしてFragmentは何よりも、タブレットと携帯端末でのUIを意識して設計されています。
Fragmentを切り替えるという設計を利用することで、以下のようなデザインをアプリに組み込むことができるのです。
詳しくは以下のDeveloperにも記載されていますので、目を通してみてください。
使ってみる1(xmlに組み込む)
先の記述の1.FragmentはActivityにレイアウトの一部(layout.xml)に部品として組み込むことができます。を実際にコードを書いてやってみます。
MainActivity.javaにボタン追加
今回作成したい画面に遷移するためのボタンを追加します。
以下の画面イメージを実装します。
面倒なのでActivityとlayoutの全文を載せます。
■R.layout.activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title_text"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/name_text"
android:hint="@string/message_name_text_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/go_button"
android:text="@string/label_go_button_go"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<Button
android:id="@+id/go_sample_list_button"
android:text="@string/label_go_sample_list_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/go_fragment_samle1"
android:text="@string/label_go_xml_fragment_sample"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
■MainActivity
public class MainActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
TextView titleText = (TextView) findViewById(R.id.title_text);
titleText.setText(getString(R.string.app_name));
Button goButton = (Button) findViewById(R.id.go_button);
goButton.setOnClickListener(this);
Button goSamleListButton = (Button) findViewById(R.id.go_sample_list_button);
goSamleListButton.setOnClickListener(new SampleListButtonClickListener(this));
Button goXmlFragmentSampleButton = (Button) findViewById(R.id.go_fragment_samle1);
goXmlFragmentSampleButton.setOnClickListener(new FragmentSample1ClickListener(getApplication()));
}
}
@Override
public void onClick(View v) {
Intent intent = new Intent(this, NextActivity.class);
intent.putExtra("titleText", ((TextView) findViewById(R.id.title_text)).getText());
startActivity(intent);
}
static class SampleListButtonClickListener implements View.OnClickListener {
private Activity activity;
public SampleListButtonClickListener(Activity activity) {
this.activity = activity;
}
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, ListSampleActivity.class);
activity.startActivity(intent);
}
}
private static class FragmentSample1ClickListener implements View.OnClickListener {
Context con;
public FragmentSample1ClickListener(Context con) {
this.con = con;
}
@Override
public void onClick(View v) {
Intent intent = FragmentSample1Activity.createInstance(con);
con.startActivity(intent);
}
}
}
新たに追加されたボタン「XML組み込みFragmentサンプルへ」を押すと、新しい画面に遷移します。
ではさっそく、新規画面を作りましょう。
今回追加される画面のイメージは以下のようなものです。
とても単純です。TextViewが一枚あり、その横に画像が表示されています。
簡単ですが、今回はこれをFragmentで実現しています!
では、新規画面の実装に入ります。
FragmentSample1Activityの作成
ルートパッケージに以下のActivityを追加
package jp.co.kenshu;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
public class FragmentSample1Activity extends Activity {
public static Intent createInstance(Context con) {
Intent intent = new Intent(con, FragmentSample1Activity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_sample1);
}
}
R.layout.activity_fragment_sample1の作成
layout下にactivity_fragment_sample1.xmlを作成してください。
名前がややこしいですが、あくまでActivityで表示するためのレイアウトです。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/fragment_sample1"
android:name="jp.co.kenshu.fragment.FragmentSample1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
FragmentSample1.javaの作成
FragmentはJavaで書きます。Activityと似たようなプロセスを経て作成できます。
jp.co.keshu.fragmentパッケージを作成し、以下のjavaを追加してください。
package jp.co.kenshu.fragment;
import android.app.Fragment;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import jp.co.kenshu.R;
public class FragmentSample1 extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_sample1, container, false);
TextView textView = (TextView) rootView.findViewById(R.id.fragment_sample1_textview);
Drawable icon = getDrawableResource(android.R.drawable.btn_star_big_on);
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
textView.setCompoundDrawables(icon, null, null, null);
return rootView;
}
@SuppressWarnings("deprecation")
public Drawable getDrawableResource(int id){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
return getActivity().getApplicationContext().getDrawable(id);
}
else {
return getResources().getDrawable(id);
}
}
}
fragment_sample1.xmlの作成
Fragmentで実際に読み込んで表示するためのlayoutファイルを記述します。
layout下に「fragment_sample1.xml」を作成してください。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/fragment_sample1_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_fragment_sample1_text"/>
</LinearLayout>
動かしてみる(xmlからFragmentをattachしたケース)
これでXMLからFragmentをアタッチしたケースの準備はOKです。
実行してみてください。
画面に先ほどのイメージがでれば成功です。
今回は、Activityのlayoutファイルに自作したFragmentを要素として組み込んだ例となります。
以下がそれに該当します。
▼activity_fragment_sample1.xml
<fragment
android:id="@+id/fragment_sample1"
android:name="jp.co.kenshu.fragment.FragmentSample1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Activityに要素として組み込む際には、以下のお約束を守る必要があります。
- nameでどこの何てFragmentかを明記する
- idで一意に特定できる識別子を与える
今回は、1が「jp.co.kenshu.fragment.FragmentSample1」であり、2がfragment_sample1なんですね。
これでActivityがonCreateで生成される際に、layout要素のFragmentがアタッチされるため、FragmentSample1.javaが実行・評価されます。
では実際に書いたFragmentを見てみます。
Fragmentはそれ独自にライフサイクルを持ちます。
しかし、FragmentはあくまでActivityの要素です。Fragmentは以下のライフサイクルに基づいて、生成されます。
※参考…http://developer.android.com/guide/components/fragments.html
Fragmentが実際に描画するレイアウトは
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
内でinflate経由でlayoutファイルをロードします。
あとは今までやってきたActivityでの実装と変わりません。
extends Fragment
今回は
android.app.Fragment
をimportしてます。
support4のfragmentを使ったとしても、記述方法は変わりませんので。
TextView textView = (TextView) rootView.findViewById(R.id.fragment_sample1_textview);
Drawable icon = getDrawableResource(android.R.drawable.btn_star_big_on);
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
textView.setCompoundDrawables(icon, null, null, null);
これは今回は遊び程度に掲載しました。
TextViewの文字列の上下左右に画像を表示できる機能です。
textView.setCompoundDrawables(icon, null, null, null);
上位の引数は左から順番に「左に表示する画像、上に表示する画像、右に表示する画像、下に表示する画像」と指定できます。全て指定するとこんな感じになりますw
textView.setCompoundDrawables(icon, icon, icon, icon);
使ってみる2(javaからaddする)
次はActivityから動的にFragmentを追加したり、更新したりする例です。
画面イメージは以下です。
MainActivity.javaにボタン追加
例によって、追加します。
MainActivityに以下のメソッドを最終行に追加してください。
public void hoge(View view) {
Intent intent = FragmentSample2Activity.newInstance(getApplicationContext(), "Fragmentをjavaからaddするサンプル");
startActivity(intent);
}
activity_main.xmlにボタン追加
<Button
android:id="@+id/go_fragment_samle2"
android:text="@string/label_go_java_add_fragment_sample"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="hoge"/>
onClickに指定した名前がリスナーメソッドとして登録されます。
Activityに定義されているhogeメソッドが今回のリスナーになるってことです。
※数あるリスナーの実装方法の中でも、この使い方は一番お勧めしません。紹介程度で出しました。
FragmentSample2Activityの作成
package jp.co.kenshu;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.ViewGroup;
import jp.co.kenshu.fragment.FragmentSample2;
public class FragmentSample2Activity extends Activity {
public static Intent newInstance(Context con, String message) {
Intent intent = new Intent(con, FragmentSample2Activity.class);
intent.putExtra("hoge", message);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_sample2);
setFragment();
}
private void setFragment() {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragment = FragmentSample2.newInstance(getIntent().getStringExtra("hoge"));
ViewGroup root = (ViewGroup)getWindow().getDecorView().findViewById(android.R.id.content);
fragmentTransaction.add(root.getId(), fragment);
fragmentTransaction.commit();
}
}
FragmentSample2.javaの作成
package jp.co.kenshu.fragment;
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Chronometer;
import android.widget.TextView;
import jp.co.kenshu.R;
public class FragmentSample2 extends Fragment{
private String message;
public static FragmentSample2 newInstance(String messagge) {
Bundle bundle = new Bundle();
bundle.putString("hoge", messagge);
FragmentSample2 fragment = new FragmentSample2();
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
message = getArguments().getString("hoge");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_sample2, container, false);
Chronometer meter = (Chronometer) rootView.findViewById(R.id.fragment_sample2_chronometer);
meter.start();
TextView textView = (TextView) rootView.findViewById(R.id.fragment_sample2_textview);
textView.setText(message);
return rootView;
}
}
fragment_sample2.xmlの作成
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/fragment_sample2_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Chronometer
android:id="@+id/fragment_sample2_chronometer"
android:format="chronometer:%s"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>