Fragmentを使ってみる(その5)ActivityからFragmentを操作

Android Studio

久しぶりにAndroidStudioを思い出しながら触っていて、ActivityからFragmentの部品(textViewとか)を扱うのってどうするのかなとなりました。今までFragmentからActivityの部品を操作したり、Fragment から別のFragment を操作したりはしていましたが、ActivityからFragmentの部品(textViewとか)を意識して操作してはなかったようでした。

ネットで検索しても、「ActivityからFragmentを操作」って記載がないようです。後で考えるとActivityの部品を扱うのと同じように扱えばいいのでわざわざ書いてないということなのでしょうか?

そこを変に考えて試行錯誤した記録です。

環境は以下の通りです。

Android Studio Dolphin | 2021.3.1
Build #AI-213.7172.25.2113.9014738, built on September 1, 2022
Runtime version: 11.0.13+0-b1751.21-8125866 aarch64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
macOS 12.6
GC: G1 Young Generation, G1 Old Generation
Memory: 1280M
Cores: 8
Registry:
external.system.auto.import.disabled=true
ide.text.editor.with.preview.show.floating.toolbar=false

やりたいことは

activity_main.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"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginBottom="200dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

    </FrameLayout>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="NG"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

fragment_no1.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:id="@+id/constraintLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#CDDC39"
    tools:context=".no1Fragment">

    <TextView
        android:id="@+id/flg1text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:tag="1"
        android:text="TextView"
        app:layout_constraintBottom_toTopOf="@+id/flg1button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/flg1button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="OK"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/flg1text" />
</androidx.constraintlayout.widget.ConstraintLayout>

no1Fragment.kt

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView


class no1Fragment : Fragment() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        return inflater.inflate(R.layout.fragment_no1, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        val flg1Btn = requireActivity().findViewById<View>(R.id.flg1button) as? Button
        val flg1Text  = requireActivity().findViewById<View>(R.id.flg1text) as? TextView
        flg1Btn?.setOnClickListener() {
            flg1Text?.text= "OK"
        }

    }
}

なお、val flg1Text = requireActivity().findViewById<View>(R.id.flg1text) as? TextViewとかの

requireActivity()はgetActivity()?でも動きます。requireActivity()とgetActivity()の違いはnullを許容しないかする かのようです。なのでgetActivity()だと?が必要になるということのようです。自動補完だとrequireActivity()しか出てこないのでrequireActivity()使う方がいい?のでしょうね。この部分は「FragmentにおけるActivityとContextの使い分け」というHPを参考にさせていただきました。

で、本題の「ActivityからFragmentを操作」のためのMainActivity.ktです。これはちゃんと動くものです。

import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.AttributeSet
import android.view.View
import android.widget.Button
import android.widget.TextView

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val flagNo1 = no1Fragment()
        if (savedInstanceState == null) {
            val transaction = supportFragmentManager.beginTransaction()
            transaction.add(R.id.container, flagNo1)
            transaction.commit()
        }
     }

    override fun onResume() {
        super.onResume()

        val flg1Text = findViewById(R.id.flg1text) as? TextView
        val button = findViewById(R.id.button) as? Button
        button?.setOnClickListener() {
            flg1Text?.text = "NG"
       }

    }

}

結論から言うと、Activty上に乗っかっているFragment の部品はActivity上の部品と同じ様に扱えばいいと言うことで val flg1Text = findViewById(R.id.flg1text) as? TextView のように指定してあげればいいだけでした。

なのに、Android デベロッパーアクティビティと通信する

フラグメントは getActivity() を使用して FragmentActivity インスタンスにアクセスでき、アクティビティのレイアウトでビューを見つけるなどのタスクを簡単に実行できます。同様に、アクティビティが findFragmentById() や findFragmentByTag() を使って FragmentManager からFragment への参照を取得することで、フラグメント内のメソッドを呼び出すこともできます。次に例を示します。

val fragment = supportFragmentManager.findFragmentById(R.id.example_fragment) as ExampleFragment

を見て、これはFragmentを指定しているからTextViewなら

val flg1Text =supportFragmentManager.findFragmentById(R.id.flg1text) as? TextView

で動かないかなと試してみました。これはエラーにはならないし、アプリの停止もしません( as? TextViewを消すと停止します)が

「常にnullだよ」と言われます。

 findFragmentById() や findFragmentByTag() を使って FragmentManager からFragment への参照を取得すると言うのは今、表示しているFragment が何か取得するために使うものの様です。idにFragment を乗っけているContenerを指定して乗っているFragmentが何か取得する様な使い方が出来ます。

val flagNo1 = supportFragmentManager.findFragmentById(R.id.container)

とするとflagNo1には

が入ってきます。

あと、Fragmentの部品を操作する場所ですが”onCreate”だと

となります。まだ準備ができていないのでidが探せないと言うことでしょうか?”onStart”とか”onResume”に記述すれば大丈夫です。

コメント

タイトルとURLをコピーしました