【Android/Kotlin】Fragmentファイルでxmlファイルの内容を取得する

目次

概要

基本的に、xmlファイルで設定したボタンといったUIは、Activityで使うことになる。
しかし、そうするのは個人的に嫌だ。なぜなら、画面表示と処理でファイルを分けたいからだ。
新しくファイルを生成する際、Activity(Frafment + ViewModel)というものがあるが、その形式で実装したい!

ソースコード

まずは全体を把握していただこう

AndroidManifest.xml

このファイルは直接修正することはしていない

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.Test"
        tools:targetApi="31">
        <activity
            android:name=".SubActivity"
            android:exported="false" />
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

activity_main.xml

このファイルも直接修正をすることはしていない

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" />

fragment_main.xml

ボタンと文字を表示するためのUIを設置している

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.main.MainFragment">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center">
        <TextView
            android:id="@+id/pushedText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAlignment="center"/>
        <Button
            android:id="@+id/decideButton"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="決定" />
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

package com.seeking_star.test

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.seeking_star.test.ui.main.MainFragment

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .replace(R.id.container, MainFragment.newInstance())
                .commitNow()
        }
    }
}

MainFragment.kt

package com.seeking_star.test.ui.main

import androidx.lifecycle.ViewModelProvider
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.widget.Button
import android.widget.RadioGroup
import android.widget.TextView
import com.seeking_star.test.R

class MainFragment : Fragment() {

    companion object {
        fun newInstance() = MainFragment()
    }

    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        val view = inflater.inflate(R.layout.fragment_main, container, false)
        val textView = view.findViewById<TextView>(R.id.pushedText)

        val decideButton = view.findViewById<Button>(R.id.decideButton)
        decideButton.setOnClickListener {
            textView.text = "ボタン押されたよ"
        }

        return view
    }
}

MainViewModel.kt

package com.seeking_star.test.ui.main

import androidx.lifecycle.ViewModel

class MainViewModel : ViewModel() {
    // TODO: Implement the ViewModel
}

デモ動画

詳細

val view = inflater.inflate(R.layout.fragment_main, container, false)

今回の根幹となるのはこの部分だ。この一行でfragmentのxmlファイルのレイアウト情報を取得する。そのために必要なものはonCreateViewメソッドの引数である以下二つだ。

LayoutInflaterクラスのinflate(int resource, ViewGroup root, boolean attachToRoot)メソッドを使用する。
このメソッドは特定のxmlファイルからviewの情報を取得できる。
引数は以下のように設定する。

引数内容
resourcexmlファイルのID
root引っ張ってくるViewの親Viewのこと。
今回はonCreateViewメソッドのcontainerを使用する
attachToRootfalseにする。
詳しくはこちら

こうすることでxmlファイルのViewの情報を格納できる。
これを元に、findViewByIdメソッドを使用してボタンなどのオブジェクトを生成できる。

これでMVVMモデルといった実装もできるだろう。

参考ページ