Fragmentを使ったサンプルアプリ

Fragmentを使ってサンプルアプリを作りました。
かなり混乱したので、勉強したことのメモです。
## 作ったもの
Fragmentでドロワーとメインコンテンツを配置し、ドロワー内のメニューを選ぶと、選んだコンテンツがメインコンテンツに表示される、というものです。

<img width="274" alt="スクリーンショット 2019-07-22 11.09.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325419/6891b91e-afc1-1435-d657-0fe06a5b7410.png">
サンプルアプリを立ち上げた時の状態です。
左上の三本線アイコンをタップすると、ドロワーが開きます。

<img width="270" alt="スクリーンショット 2019-07-22 11.09.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325419/d11388a9-b61d-8314-7dc5-3cbdc2234089.png">
ドロワーが開いた状態です。
今回はドロワー内にListViewでメニューアイテムを表示しています。
ドロワー内でメニューを選択すると、選択したコンテンツがメインコンテンツに表示されます。

<img width="270" alt="スクリーンショット 2019-07-22 11.09.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325419/b03ecde7-ba3e-6d80-e210-2e9966eb9adc.png">
ドロワーで選択したメニューが表示された状態です。

## 構成
<img width="253" alt="スクリーンショット 2019-07-22 11.17.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325419/998ddc70-f400-649d-1f19-42b69016211c.png">
構成はこんな感じです。
各ファイルの内容と役割を補足すると、
・**MainActivity**:フラグメントの配置、切り替えなど。
・**ListFragment.kt**:ドロワーのビューを作る。
・**Content1Fragment.kt**と**Content2Fragment.kt**:それぞれContent1、Content2をフラグメントに貼り付け、ビューを作る。
・**activity_main.xml**:全体のレイアウトを定義する。ドロワーとメインコンテンツの領域をここで決めている。
・**content1.xml**と**content2.xml**:それぞれcontent1とcontent2のレイアウトを定義。
・**list.xml**:ドロワーに表示するリストのレイアウトを定義。
・**list_content_row.xml**:リストの行に相当する部分。

## 仕組み
ポイントと思わしき部分です。

### activity_main.xml
```xml:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.sampleapplication.sample.fragmentsample.MainActivity">

    <!--コンテンツ（左側）-->
    <FrameLayout
        android:id="@+id/maincontent"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </FrameLayout>

    <!--ドロワー（右側）-->
    <FrameLayout
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="?android:attr/colorBackground">
        <fragment
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/contentList"
            android:name="com.example.sampleapplication.sample.fragmentsample.ListFragment"
            tools:layout="@layout/list"
            ></fragment>
    </FrameLayout>

</android.support.v4.widget.DrawerLayout>
```
DrawerLayoutを使っています。
ここで左側のコンテンツ部分、右側のドロワー部分を定義しています。
左側のコンテンツ部分は、選択されたメニューによってフラグメントを入れ替えるので、アクティビティの方から動的にフラグメントを配置することにしました。
右側のドロワー部分に関しては、常に同じものを表示し続けるので、フラグメントをここで定義しています。


### Contant1Fragment.kt
```kotlin:Content1Fragment.kt
package com.example.sampleapplication.sample.fragmentsample

import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

class Content1Fragment : Fragment() {

    // ビューを作る。
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        // content1.xmlをactivity_main.xmlのcontainerに配置する
        // inflaterは特定の場所に何かを差し込むもの。
        val view = inflater.inflate(R.layout.content1,container,false)
        return view
    }
}

fun newContent1Fragment():Content1Fragment{
    //インスタンス生成
    val fragment1 = Content1Fragment()
    return fragment1
}
```
※Content2Fragment.ktも同じような内容です。
フラグメントとして配置するビューを作っています。
onCreateView内の

```kotlin:
 val view = inflater.inflate(R.layout.content1,container,false)
```
この部分で、どのビューをフラグメントとして配置するのかを指定しています（この場合はcontent1.xmlです）。

newContent1Fragmentを呼び出すことでフラグメントを配置します。



### ListFragment.kt
```kotlin:ListFragment.kt
package com.example.sampleapplication.sample.fragmentsample

import android.content.Context
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ListView

class ListFragment : Fragment(){

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        if(context !is OnItemClickListner)
            throw RuntimeException("リスナーがないよ")
    }

    // ビューを作る。
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        // list.xmlをListFragmentに持ってくる
        val listframe = inflater!!.inflate(R.layout.list,container,false)

        // アイテム一覧を表示するListView（list.xmlのidがcontentListのListView）を指定
        val listView = listframe.findViewById(R.id.contentList) as ListView

        // アイテム一覧に表示するアイテム。
        val items = arrayOf("コンテンツ１","コンテンツ２")

        // アダプター
        val adapter = ArrayAdapter<String>(this.context,R.layout.list_content_row,R.id.contentName,items)

        // アダプターを使ってListviewにアイテムを表示
        listView.adapter = adapter

        // リストの中のアイテムが選択されたら、選択されたアイテム名をリスナーに知らせる
        listView.setOnItemClickListener{parent, view, position, id ->
            val selectedItem = items[position]
            val listner = context as? OnItemClickListner
            listner?.onItemClicked(contentName = selectedItem)
        }
        return listframe
    }

    interface OnItemClickListner{
        fun onItemClicked(contentName: String)
    }
}

fun newListFragment():ListFragment{
    //インスタンス生成
    val fragment_list = ListFragment()
    return fragment_list
}
```
左側、ドロワーにリストを表示するフラグメントです。
ListViewを使ってフラグメント上にリストを配置します。
また、リスト内の行（＝アイテム）をクリックした時にフラグメントを切り替えたいので、クリックイベントをとる処理もここに書いています。リスト内のアイテムがクリックされたら、どこのアイテムがクリックされたのかをリスナーに渡します。

### MainActivity.kt
```kotlin:MainActivity.kt
package com.example.sampleapplication.sample.fragmentsample

import android.content.res.Configuration
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v4.widget.DrawerLayout
import android.support.v7.app.ActionBarDrawerToggle
import android.view.MenuItem

class MainActivity : AppCompatActivity(),ListFragment.OnItemClickListner{

    override fun onItemClicked(contentName: String) {
        val fragmentTransaction = supportFragmentManager.beginTransaction()
        fragmentTransaction.addToBackStack(null)

        if(contentName == "コンテンツ１"){
            fragmentTransaction
                    .replace(R.id.maincontent,newContent1Fragment(),"content1Fragment")
                    .commit()
        }else if(contentName == "コンテンツ２"){
            fragmentTransaction
                    .replace(R.id.maincontent,newContent2Fragment(),"content2Fragment")
                    .commit()
        }

        closeDrawer()

    }

    // ドロワーの状態を管理する
    private var drawerToggle:ActionBarDrawerToggle?=null

    // 最初に実行される
    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        setViews()

        // 動的にフラグメントを配置（content1, list。この二つはデフォルトとする)
        initial_setFragment()

    }

    // アクティビティの生成が終わった後に呼ばれる
    override fun onPostCreate(savedInstanceState: Bundle?) {
        super.onPostCreate(savedInstanceState)

        // ドロワーのトグルの状態を同期する
        drawerToggle?.syncState()

    }

    // 画面が回転した時とかに呼ばれる
    override fun onConfigurationChanged(newConfig: Configuration?) {
        super.onConfigurationChanged(newConfig)

        // 状態の変化をdrawerに伝える
        drawerToggle?.onConfigurationChanged(newConfig)
    }

    // はじめに設置するフラグメント
    fun initial_setFragment(){
        //フラグメント配置（content1）
        if (supportFragmentManager.findFragmentByTag("content1Fragment") == null){
            supportFragmentManager.beginTransaction()
                    .replace(R.id.maincontent,newContent1Fragment(),"content1Fragment")
                    .commit()
        }

        //フラグメント配置（list）
        if (supportFragmentManager.findFragmentByTag("listFragment") == null){
            supportFragmentManager.beginTransaction()
                    .add(R.id.contentList,newListFragment(),"listFragment")
                    .commit()
        }
    }


    // アップバーの設定
    private fun setupDrawer(drawer:DrawerLayout){
        val toggle = ActionBarDrawerToggle(this,drawer,R.string.app_name,R.string.app_name)
        
        toggle.isDrawerIndicatorEnabled = true
        drawer.addDrawerListener(toggle)
        drawerToggle = toggle

        // アクションバーの設定
        supportActionBar?.apply {
            setDisplayHomeAsUpEnabled(true)
            setHomeButtonEnabled(true)
        }
    }


    // オプションメニューがタップされた時に呼ばれる
    override fun onOptionsItemSelected(item: MenuItem?): Boolean {

        // ドロワーに伝える
        if(drawerToggle?.onOptionsItemSelected(item) == true){
            return true
        }else {
            return super.onOptionsItemSelected(item)
        }
    }

    // レイアウトを行う
    private fun setViews(){
        setContentView(R.layout.activity_main)

        // ドロワーから探す（左側）
        val drawerLayout = findViewById<DrawerLayout>(R.id.drawerLayout)

        // ドロワーが見つかったら実行
        if(drawerLayout != null){
            setupDrawer(drawerLayout)
        }
    }

    // ドロワーを閉じる
    fun closeDrawer(){
        val drawer_layout = findViewById<DrawerLayout>(R.id.drawerLayout)
        val toggle = ActionBarDrawerToggle(this,drawer_layout,R.string.app_name,R.string.app_name)
        drawer_layout.addDrawerListener(toggle)
        drawer_layout.closeDrawers()
    }

}
```

onCreateからinitial_setFragmentを呼び出すことで、初期処理としてフラグメントを配置しています（listとcontent1）。
onItemClickedでリストのアイテムがクリックされた時に選択されたアイテム名を受け取っています。
受け取ったアイテム名に応じて、右側に表示するフラグメントを切り替えています。
表示するフラグメントを切り替えた後、ドロワーは閉じたいので、closeDrawerを呼んでいます。

## 最後に
難しかったです・・・
間違っている部分、おかしな点があればぜひご指摘いただきたいです・・・！


## 参考
・[基本からしっかり身につくAndroidアプリ開発入門 Android Studio 3対応 (「黒帯エンジニア」シリーズ)](https://www.amazon.co.jp/%E5%9F%BA%E6%9C%AC%E3%81%8B%E3%82%89%E3%81%97%E3%81%A3%E3%81%8B%E3%82%8A%E8%BA%AB%E3%81%AB%E3%81%A4%E3%81%8FAndroid%E3%82%A2%E3%83%97%E3%83%AA%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80-Android-Studio-3%E5%AF%BE%E5%BF%9C-%E3%80%8C%E9%BB%92%E5%B8%AF%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%80%8D%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA/dp/479739580X/ref=pd_sbs_14_4/357-3401548-0928524?_encoding=UTF8&pd_rd_i=479739580X&pd_rd_r=94a14677-2dca-4d5f-af68-8f829757d8c8&pd_rd_w=4OCrS&pd_rd_wg=gATGt&pf_rd_p=ad2ea29d-ea11-483c-9db2-6b5875bb9b73&pf_rd_r=EJ5EY3HQMB2AVA2JT4A7&psc=1&refRID=EJ5EY3HQMB2AVA2JT4A7)