2019年7月3日水曜日

BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 編 : ViewPager + FragmentPagerAdapter での onResume() の挙動

ViewPager + FragmentPagerAdapter での setVisibleUserHint の挙動」の BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 編です。


FragmentPagerAdapter の getItem() で返す Fragment に次のようにログを入れました。 class SimpleFragment : Fragment() { override fun onStart() { super.onStart() println("$position : onStart") } override fun onResume() { super.onResume() println("$position : onResume") } override fun onPause() { super.onPause() println("$position : onPause") } override fun onStop() { super.onStop() println("$position : onStop") } }

1. レイアウトより前のタイミング(例えば onCreate())で adapter をセットしている場合 class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewPager.adapter = MyPager(supportFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) } } この場合、ログは次のようになります。 System.out: 0 : onStart System.out: 1 : onStart System.out: 0 : onResume つまり
  • 1. position 0 の Fragment の onStart()
  • 2. position 1 の Fragment の onStart()
  • 3. position 0 の Fragment の onResume()
という流れです。



2. レイアウトより前のタイミング(例えば onCreate())で adapter をセットし、currentItem を変更している場合

onCreate() で ViewPager に adapter をセットした後 currentItem を 1 にしてみます。 class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewPager.adapter = MyPager(supportFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) viewPager.currentItem = 1 } } この場合のログはこうなります。 System.out: 1 : onStart System.out: 0 : onStart System.out: 2 : onStart System.out: 1 : onResume つまり
  • 1. position 1 の Fragment の onStart()
  • 2. position 0 の Fragment の onStart()
  • 3. position 2 の Fragment の onStart()
  • 4. position 1 の Fragment の onResume()
という流れです。



3. レイアウトより後のタイミングで adapter をセットしている場合 class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Handler().postDelayed( { viewPager.adapter = MyPager(supportFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) }, 1000 ) } } onCreate() から1秒後に adapter をセットしてみます。この場合ログは次のようになります。 System.out: 0 : onStart System.out: 1 : onStart System.out: 0 : onResume 「1. レイアウトより前のタイミング(例えば onCreate())で adapter をセットしている場合」と同じですね。
  • 1. position 0 の Fragment の onStart()
  • 2. position 1 の Fragment の onStart()
  • 3. position 0 の Fragment の onResume()
という流れです。



4. レイアウトより後のタイミングで adapter をセットし、currentItem を変更している場合

onCreate() から1秒後に adapter をセットし、currentItem を 1 にしてみます。 class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Handler().postDelayed( { viewPager.adapter = MyPager(supportFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) viewPager.currentItem = 1 }, 1000 ) } } この場合ログは次のようになります。 System.out: 0 : onStart System.out: 1 : onStart System.out: 0 : onResume System.out: 2 : onStart System.out: 0 : onPause System.out: 1 : onResume 「2. レイアウトより前のタイミング(例えば onCreate())で adapter をセットし、currentItem を変更している場合」と同じになりません!

まず「3. レイアウトより後のタイミングで adapter をセットしている場合」と同じことが起こります。

つまり
  • 1. position 0 の Fragment の onStart()
  • 2. position 1 の Fragment の onStart()
  • 3. position 0 の Fragment の onResume()
という流れが最初にあります。

その後
  • 4. position 2 の Fragment の onStart()
  • 5. position 0 の Fragment の onPause()
  • 6. position 1 の Fragment の onResume()
という流れになります。

adapter をセットしたタイミングで 1 〜 3 までの流れが起こり、currentItem を 1 にしたタイミングで 4 〜 6 の流れが起こります。

このように、レイアウトよりも後のタイミングで adapter をセットすると、その時点で position 0 の Fragment を PrimaryItem として onResume() まで呼ばれてしまいます。

そのため、レイアウトよりも後のタイミングで adapter をセットして currentItem を 0 以外に変更する場合、「Fragment がユーザーに表示されたタイミングで xx したい」処理のトリガーとして onResume() を使うには注意が必要です。

adapter がセットされている間の onResume() に反応してしまうと、その後 currentItem が変更されて実際にはユーザーにはほぼ見えない Fragment でも処理が走ってしまうため、adapter がセットされている間だけフラグを立てておいて onResume() で反応しないような工夫が必要になります。


0 件のコメント:

コメントを投稿