LoginSignup
41
22

More than 3 years have passed since last update.

Android FragmentContainerViewとは

Last updated at Posted at 2019-11-02

はじめに

AndroidでFragmentを表示する際のコンテナに、何を利用していますか?
一般的なコンテナとして<FrameLayout>を、Navigation ComponentのFragmentのコンテナとして<fragment>を利用できます。

これからはandroidx.fragment 1.2.0から導入されたFragmentContainerViewを利用するべきだと考えています。
この記事ではFragmentContainerViewを調査した結果を記載しています。

FragmentContainerViewの概要

ActivityでFragmentを表示する際にActivity側でFragmentのコンテナとなるViewを作成する必要があります。
コンテナとなり得るViewの1つがFragmentContainerViewです。
xmlで記載する場合の例が下記です。

example.xml
<androidx.fragment.app.FragmentContainerView
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

FragmentContainerViewはFragmentのコンテナに特化したViewとなるので、FrameLayoutのようにそれ以外の用途で利用することはできません。

FragmentContainerViewとFrameLayoutの比較

Android Dev Summit 2019でFragmentContainerViewとFrameLayoutを比較した発表がありました。Fragments: Past, Present, and Future (Android Dev Summit '19)
この発表で取り上げている遷移アニメーション時のZ orderingのissueについて記載します。

Z orderingのissue

準備

Z orderingのissueを再現するために、ホストとなるActivity、3つのFragment(FirstFragment、SecondFragment、ThirdFragment)、アニメーションのレイアウトを作成します。
ActivityはFragmentを表示する処理のみです。

MainActivity.kt
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        val fragmentTransaction = supportFragmentManager
            .beginTransaction()
            .setCustomAnimations(R.anim.nav_enter, R.anim.nav_exit)
            .replace(R.id.container, FirstFragment())
            .addToBackStack(null)
        fragmentTransaction.commit()
    }
}

activity_main.xmlはFragmentのコンテナとなるViewのみとなります。
比較のため、まずはFrameLayoutを置きます。

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

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".activity.MainActivity">

        <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

<!--    <androidx.fragment.app.FragmentContainerView-->
<!--            android:id="@+id/container"-->
<!--            android:layout_width="match_parent"-->
<!--            android:layout_height="match_parent" />-->

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

次にFragmentです。遷移アニメーションを確認するために、ボタンを押下すると次のFragmentに遷移させています。処理が同じであるためFirstFragmentのみ記載します。

FirstFragment
class FirstFragment : Fragment() {

    @SuppressLint("CheckResult")
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        super.onCreateView(inflater, container, savedInstanceState)

        val binding = DataBindingUtil.inflate<FragmentFirstBinding>(
            inflater,
            R.layout.fragment_first,
            container,
            false
        )
        binding.apply {
            lifecycleOwner = this@FirstFragment
            destinationButton.clicks().subscribe {
                val fragmentTransaction = parentFragmentManager
                    .beginTransaction()
                    .setCustomAnimations(R.anim.nav_enter, R.anim.nav_exit)
                    .replace(R.id.container, SecondFragment())
                    .addToBackStack(null)
                fragmentTransaction.commit()
            }
        }

        return binding.root
    }
}

次にアニメーションです。上記のsetCustomAnimationsの引数にしていしているEnterアニメーションとExitアニメーションです。

nav_enter.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/decelerate_interpolator"
    android:shareInterpolator="true">
    <translate
        android:duration="400"
        android:fromXDelta="100%"
        android:fromYDelta="0%"
        android:toXDelta="0%"
        android:toYDelta="0%" />
</set>
nav_exit
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/decelerate_interpolator"
    android:shareInterpolator="true"
    android:zAdjustment="bottom">
    <translate
        android:duration="400"
        android:fromXDelta="0%"
        android:fromYDelta="0%"
        android:toXDelta="-50%"
        android:toYDelta="0%" />
</set>

FragmentのコンテナがFrameLayoutの場合の結果

FragmentのコンテナがFragmentContainerViewの場合の結果

activity_main.xmlのFrameLayoutをコメントアウトし、FragmentContainerViewのコメントアウトを外し実行します。

考察

FrameLayoutの場合、まず次のFragmentのEnterアニメーションが始まり、次に現在のFragmentのExitアニメーションが始まるので、このような結果となっています。
これを防ぐために、nav_exit.xmlandroid:zAdjustment="bottom"を指定(Z ordering)することにより解決を試みていますが、効いていません。これがZ orderingのissueです。

FragmentContainerViewの場合、まず現在のFragmentのExitアニメーションが始まり、次に次のFragmentのEnterアニメーションが始まるので、このような結果となっています。

おまけ

Navigation ComponentのFragmentのコンテナとして<fragment>がよく利用されます。
このコンテナをFragmentContainerViewに置き換えることができます。
実際にFragmentContainerViewを利用している例をこちらの記事に記載しています。
Android NavigationとSharedViewModel

41
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
22