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



構成

仕組み
ポイントと思わしき部分です。
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
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内の
val view = inflater.inflate(R.layout.content1,container,false)
この部分で、どのビューをフラグメントとして配置するのかを指定しています(この場合はcontent1.xmlです)。
newContent1Fragmentを呼び出すことでフラグメントを配置します。
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("コンテンツ1","コンテンツ2")
// アダプター
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
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 == "コンテンツ1"){
fragmentTransaction
.replace(R.id.maincontent,newContent1Fragment(),"content1Fragment")
.commit()
}else if(contentName == "コンテンツ2"){
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対応 (「黒帯エンジニア」シリーズ)