LoginSignup
37
30

More than 5 years have passed since last update.

DroidKaigiのアプリのSingle Activity(Fragment)でハマったところメモ

Last updated at Posted at 2019-03-02

他にもSingle Activity関連はありましたが、とりあえず大きなところでいうとこれが勉強になったので書いておきます。

セッションのリストに戻った時にスクロールの位置が戻る問題

結論から言うとFragmentのライフサイクルへの理解が足りませんでした。

起こった問題

セッションの詳細の画面から、セッションのリストに戻った時にスクロールの位置が戻る問題がありました。
https://github.com/DroidKaigi/conference-app-2019/issues/33

問題があった当時のコードはこれです。savedInstanceStateがないときにcommitしています。これの何が問題なのでしょうか?

https://github.com/DroidKaigi/conference-app-2019//blob/468db8acfd4912cafae3b2f745af0a225e288313/feature/session/src/main/java/io/github/droidkaigi/confsched2019/session/ui/SessionPageFragment.kt より

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        setupBottomSheet(savedInstanceState)
    private fun setupBottomSheet(savedInstanceState: Bundle?) {
        if (savedInstanceState == null) { // savedInstanceStateがないときだけcommit
            val fragment: Fragment = when (val tab = SessionPage.pages[args.tabIndex]) {
...
                SessionPage.Favorite -> {
                    BottomSheetFavoriteSessionsFragment.newInstance()
                }
            }

            childFragmentManager
                .beginTransaction()
                .replace(R.id.sessions_sheet, fragment)
                .disallowAddToBackStack()
                .commit()
        }

原因ととりあえずの解決策

なぜかというとFragmentがバックスタックから戻ってきたときはsavedInstanceStateがnullになるため、replaceしてしまうためです。

そこでこういうPRが届きました。(https://github.com/Akihiroooo/conference-app-2019/blob/07ee9c585d39c33f7872f3055dd63d89de5c7fee/feature/session/src/main/java/io/github/droidkaigi/confsched2019/session/ui/SessionPageFragment.kt#L127 )

childFragmentManager.findFragmentByTag(tab.title)によりFragmentManager判定します

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        setupBottomSheet(savedInstanceState)
    private fun setupBottomSheet(savedInstanceState: Bundle?) {
        // suppress fragment replacement
        val tab = SessionPage.pages[args.tabIndex]
        if (savedInstanceState == null &&
            childFragmentManager.findFragmentByTag(tab.title) == null
        ) {

最終的な解決策

ここで自分がうまく動いた!とツイートします
そこで以下のツッコミが入ります

ここで以下のことを学びました。
バックスタックから戻ってきたときはonCreateは呼ばれずonCreateViewやonActivityCreatedは呼ばれる。
孫フラグメントを作るときはFragmentのonCreateでcommit()をすることで、フラグメントが無駄にまた作られることを防げる。

image.png
https://github.com/xxv/android-lifecycle より

最終的に以下のようにonCreateで実装すれば完成です!
https://github.com/DroidKaigi/conference-app-2019/blob/e714989139f182fe499b1b3d4d4a4f5e8b9cd9cc/feature/session/src/main/java/io/github/droidkaigi/confsched2019/session/ui/SessionPageFragment.kt#L91

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (savedInstanceState == null) {
            setupSessionsFragment()
        }
    }
...
    private fun setupSessionsFragment() {
        val tab = SessionPage.pages[args.tabIndex]
        val fragment: Fragment = when (tab) {
...
            SessionPage.Favorite -> {
                BottomSheetFavoriteSessionsFragment.newInstance()
            }
        }

        childFragmentManager
            .beginTransaction()
            .replace(R.id.sessions_sheet, fragment, tab.title)
            .disallowAddToBackStack()
            .commit()
    }
37
30
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
37
30