Androidのお勉強  第三回　フラグメントの使用

# Androidのお勉強 第三回 フラグメントの使用

第三回はフラグメントの使い方を紹介します。
<font color="red">**※編集中です**</font>

* 第一回　[環境構築、AndroidPJの解説、サンプルの作成、イベントの実装、画面遷移(Intent)](http://qiita.com/yu_eguchi/items/b7cf67346c323ffca2c8)
* 第二回  [ListViewと独自Adapterについて](http://qiita.com/yu_eguchi/items/65311af1c9fc0bff0cb0)
* 第三回　フラグメントの使用
* 第四回　復元処理の重要性について
* 第五回　非同期通信の重要性、ANRと戦う
* 第六回　独自レイアウトを作ろう。Viewの拡張の注意点。OnMeasureやViewのライフサイクルについて学ぶ
* 第七回　API x SQLiteDB連動アプリを作成してみよう

# フラグメントって??

FragmentはAndroid 3.x系から追加されたコンポーネントです。
従来は画面を作成するために、「ViewとActivity」を画面ごとに作成してきました。
似たような画面が複数必要な場合であっても、「ViewとロジックがセットになったActivity」を再度作らなければいけません。

つまり、<font color="red">**「画面の再利用」ができなかった**</font>わけです。
<font color="red">※layoutでincludeする手法は**「Viewの再利用しかできません」**。**「ロジックを含んだViewの再利用」**ができなかったって意味です。</font>

そんな中現れたのがFragmentです。

<font color="red">Fragmentが「**「ロジックを含んだViewの再利用」**」を可能にします</font>。

1. FragmentはActivityにレイアウトの一部(layout.xml)に部品として組み込むことができます。
2. javaのソース(Activity)内で、動的に追加、削除ができます。

そしてFragmentは何よりも、タブレットと携帯端末でのUIを意識して設計されています。

Fragmentを切り替えるという設計を利用することで、以下のようなデザインをアプリに組み込むことができるのです。

![alt](http://developer.android.com/images/fundamentals/fragments.png)

詳しくは以下のDeveloperにも記載されていますので、目を通してみてください。

http://developer.android.com/guide/components/fragments.html

# 使ってみる1(xmlに組み込む)

先の記述の**1.FragmentはActivityにレイアウトの一部(layout.xml)に部品として組み込むことができます**。を実際にコードを書いてやってみます。

## MainActivity.javaにボタン追加

今回作成したい画面に遷移するためのボタンを追加します。
以下の画面イメージを実装します。

![fra1.png](https://qiita-image-store.s3.amazonaws.com/0/78857/22b00407-caae-a8cd-63e6-0608344f4174.png)

面倒なので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サンプルへ**」を押すと、新しい画面に遷移します。

ではさっそく、新規画面を作りましょう。

今回追加される画面のイメージは以下のようなものです。

![frafra1.png](https://qiita-image-store.s3.amazonaws.com/0/78857/f7cf9f8f-d324-a668-c9c6-5b836bef37cc.png)

とても単純です。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**を作成してください。
名前がややこしいですが、あくまで<font color="red">**Activityで表示するためのレイアウト**</font>です。

```
<?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です。
実行してみてください。

画面に先ほどのイメージがでれば成功です。

今回は、<font color="red">**Activityのlayoutファイルに自作したFragmentを要素として組み込んだ例**</font>となります。

以下がそれに該当します。

▼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に要素として組み込む際には、以下のお約束を守る必要があります。

1. nameでどこの何てFragmentかを明記する
2. idで一意に特定できる識別子を与える

今回は、1が「jp.co.kenshu.fragment.FragmentSample1」であり、2がfragment_sample1なんですね。

これでActivityがonCreateで生成される際に、layout要素のFragmentがアタッチされるため、FragmentSample1.javaが実行・評価されます。

では実際に書いたFragmentを見てみます。

Fragmentはそれ独自にライフサイクルを持ちます。
しかし、FragmentはあくまでActivityの要素です。Fragmentは以下のライフサイクルに基づいて、生成されます。

![alt](http://developer.android.com/images/fragment_lifecycle.png)

※参考…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);
```

上位の引数は左から順番に「左に表示する画像、上に表示する画像、右に表示する画像、下に表示する画像」と指定できます。全て指定するとこんな感じになりますｗ

```
textView.setCompoundDrawables(icon, icon, icon, icon);
```

![stars.png](https://qiita-image-store.s3.amazonaws.com/0/78857/d6e8315c-4a5b-d543-4c2a-33dc6ff421b0.png)


# 使ってみる2(javaからaddする)

次はActivityから動的にFragmentを追加したり、更新したりする例です。
画面イメージは以下です。

![fff.png](https://qiita-image-store.s3.amazonaws.com/0/78857/4dfd2330-c030-a50b-a477-3f26ae7aa35c.png)



## 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>
```

# 動かしてみる2(javaからaddする)

# フラグメントの使い時についての考察