メモ > 技術 > IDE: AndroidStudio > アプリの作成(XMLレイアウト / ドロワーメニュー)
アプリの作成(XMLレイアウト / ドロワーメニュー)
以下で新規にプロジェクトを作成
プロジェクトの選択: Empty Activity
プロジェクトの名前: drawer
ビューバインディングを使えるようにする
build.gradle を変更したら「Sync Now」をクリック
メニュー用のフラグメントを作成する
プロジェクトウインドウのツリーで「app」を右クリックし、
「New → Fragment → Fragment(Blank)」を選択する
表示されたダイアログで「Fragment Name」で「MenuFragment」と入力して、あとはデフォルトのまま「Finish」ボタンを押す
プロジェクトウインドウのツリーに「MenuFragment.kt」と「fragment_menu.xml」が追加される
fragment_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center_horizontal"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/firstButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="First" />
<Button
android:id="@+id/secondButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="Second" />
<Button
android:id="@+id/thirdButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="Third" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
MenuFragment
package org.refirio.drawer
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.fragment.app.Fragment
class MenuFragment() : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_menu, container, false)
val firstButton = view.findViewById<Button>(R.id.firstButton)
firstButton.setOnClickListener {
val listener = context as? OnButtonClickListener
listener?.onFirstButtonClicked()
}
val secondButton = view.findViewById<Button>(R.id.secondButton)
secondButton.setOnClickListener {
val listener = context as? OnButtonClickListener
listener?.onSecondButtonClicked()
}
val thirdButton = view.findViewById<Button>(R.id.thirdButton)
thirdButton.setOnClickListener {
val listener = context as? OnButtonClickListener
listener?.onThirdButtonClicked()
}
return view
}
interface OnButtonClickListener {
fun onFirstButtonClicked()
fun onSecondButtonClicked()
fun onThirdButtonClicked()
}
}
コンテンツ用のフラグメントを作成する
プロジェクトウインドウのツリーで「app」を右クリックし、
「New → Fragment → Fragment(Blank)」を選択する
表示されたダイアログで「Fragment Name」で「FirstFragment」と入力して、あとはデフォルトのまま「Finish」ボタンを押す
プロジェクトウインドウのツリーに「FirstFragment.kt」と「fragment_first.xml」が追加される
fragment_first.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="First"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
FirstFragment.kt
package org.refirio.drawer
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
class FirstFragment() : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_first, container, false)
return view
}
}
同様に SecondFragment と ThirdFragment を作成する
(SecondFragment.kt と fragment_second.xml と ThirdFragment.kt と fragment_third.xml が追加される。)
メインアクティビティとそのレイアウトを実装する
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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=".MainActivity">
<!-- メインコンテンツ -->
<FrameLayout
android:id="@+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</FrameLayout>
<!-- ドロワーコンテンツ -->
<FrameLayout
android:id="@+id/drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="?android:attr/colorBackground">
<fragment
android:id="@+id/fragment"
android:name="org.refirio.drawer.MenuFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/fragment_menu" />
</FrameLayout>
</androidx.drawerlayout.widget.DrawerLayout>
MainActivity.kt
package org.refirio.drawer
import android.content.res.Configuration
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.drawerlayout.widget.DrawerLayout
class MainActivity : AppCompatActivity(), MenuFragment.OnButtonClickListener {
// ドロワーの状態操作用オブジェクト
private var drawerToggle : ActionBarDrawerToggle? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (supportFragmentManager.findFragmentByTag("ContentFragment") == null) {
supportFragmentManager.beginTransaction()
.add(R.id.container, FirstFragment(), "ContentFragment")
.commit()
}
// レイアウトからドロワーを探す
val drawerLayout = findViewById<DrawerLayout>(R.id.drawerLayout)
// ドロワーを作成する
val toggle = ActionBarDrawerToggle(this, drawerLayout, R.string.app_name, R.string.app_name)
toggle.isDrawerIndicatorEnabled = true
drawerLayout.addDrawerListener(toggle)
// ドロワーの状態を保持
drawerToggle = toggle
// ドロワーの設定を行う
supportActionBar?.apply {
setDisplayHomeAsUpEnabled(true)
setHomeButtonEnabled(true)
}
}
override fun onFirstButtonClicked() {
supportFragmentManager.beginTransaction()
.replace(R.id.container, FirstFragment())
.addToBackStack(null)
.commit()
}
override fun onSecondButtonClicked() {
supportFragmentManager.beginTransaction()
.replace(R.id.container, SecondFragment())
.addToBackStack(null)
.commit()
}
override fun onThirdButtonClicked() {
supportFragmentManager.beginTransaction()
.replace(R.id.container, ThirdFragment())
.addToBackStack(null)
.commit()
}
// アクティビティの生成が終わった後に呼ばれる
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
// ドロワーのトグルの状態を回復する
drawerToggle?.syncState()
}
// 画面構成が変わったときに呼ばれる
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// 状態の変化をドロワーに伝える
drawerToggle?.onConfigurationChanged(newConfig)
}
// オプションメニューがタップされたときに呼ばれる
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// ドロワーに伝える
if (drawerToggle?.onOptionsItemSelected(item) == true) {
return true
} else {
return super.onOptionsItemSelected(item)
}
}
}
ここまででいったん完成
引き続き、
・縦表示ならドロワーメニュー
・横表示ならサイドメニュー
としてみる
横表示用にレイアウトを追加する
activity_main.xml を開き、「Design」タブで表示する
タブ内のツールバーから「Orientation in Editor → Create Landscape Variation」を選択する
以下のファイルが作成される。内容はもとの activity_main.xml と同じものになる
app/src/main/res/layout-land/activity_main.xml
このファイルを以下のように修正する
(rootのIDをdrawerLayoutから別のものに変えると、「Configurations for activity_main.xml must agree on the root element's ID.」のエラーになった
このエラーを回避するために、rootのDrawerLayoutは触らずにLinearLayoutを追加している
正しい方法かどうかは不明)
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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=".MainActivity">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<!-- ドロワーコンテンツ -->
<FrameLayout
android:id="@+id/drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="?android:attr/colorBackground">
<fragment
android:id="@+id/fragment"
android:name="org.refirio.drawer.MenuFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/fragment_menu" />
</FrameLayout>
<!-- メインコンテンツ -->
<FrameLayout
android:id="@+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</FrameLayout>
</LinearLayout>
</androidx.drawerlayout.widget.DrawerLayout>
MainActivity.kt を以下のように変更する
package org.refirio.drawer
import android.content.res.Configuration
import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.drawerlayout.widget.DrawerLayout
class MainActivity : AppCompatActivity(), MenuFragment.OnButtonClickListener {
// ドロワーの状態操作用オブジェクト
private var drawerToggle : ActionBarDrawerToggle? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (supportFragmentManager.findFragmentByTag("ContentFragment") == null) {
supportFragmentManager.beginTransaction()
.add(R.id.container, FirstFragment(), "ContentFragment")
.commit()
}
val orientation = resources.configuration.orientation
// 縦向きの場合
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
val drawerLayout = findViewById<DrawerLayout>(R.id.drawerLayout)
setupDrawer(drawerLayout)
}
}
override fun onFirstButtonClicked() {
supportFragmentManager.beginTransaction()
.replace(R.id.container, FirstFragment())
.addToBackStack(null)
.commit()
}
override fun onSecondButtonClicked() {
supportFragmentManager.beginTransaction()
.replace(R.id.container, SecondFragment())
.addToBackStack(null)
.commit()
}
override fun onThirdButtonClicked() {
supportFragmentManager.beginTransaction()
.replace(R.id.container, ThirdFragment())
.addToBackStack(null)
.commit()
}
// アクティビティの生成が終わった後に呼ばれる
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
// ドロワーのトグルの状態を回復する
drawerToggle?.syncState()
}
// 画面構成が変わったときに呼ばれる
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// 状態の変化をドロワーに伝える
drawerToggle?.onConfigurationChanged(newConfig)
}
// オプションメニューがタップされたときに呼ばれる
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// ドロワーに伝える
if (drawerToggle?.onOptionsItemSelected(item) == true) {
return true
} else {
return super.onOptionsItemSelected(item)
}
}
private fun setupDrawer(drawerLayout: DrawerLayout) {
// ドロワーを作成する
val toggle = ActionBarDrawerToggle(this, drawerLayout, R.string.app_name, R.string.app_name)
toggle.isDrawerIndicatorEnabled = true
drawerLayout.addDrawerListener(toggle)
// ドロワーの状態を保持
drawerToggle = toggle
// ドロワーの設定を行う
supportActionBar?.apply {
setDisplayHomeAsUpEnabled(true)
setHomeButtonEnabled(true)
}
}
}