2019年7月3日水曜日

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

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


FragmentPagerAdapter の getItem() で返す Fragment に次のようにログを入れました。
  1. class SimpleFragment : Fragment() {  
  2.   
  3.     override fun onStart() {  
  4.         super.onStart()  
  5.         println("$position : onStart")  
  6.     }  
  7.   
  8.     override fun onResume() {  
  9.         super.onResume()  
  10.         println("$position : onResume")  
  11.     }  
  12.   
  13.     override fun onPause() {  
  14.         super.onPause()  
  15.         println("$position : onPause")  
  16.     }  
  17.   
  18.     override fun onStop() {  
  19.         super.onStop()  
  20.         println("$position : onStop")  
  21.     }  
  22. }  


1. レイアウトより前のタイミング(例えば onCreate())で adapter をセットしている場合
  1. class MainActivity : AppCompatActivity() {  
  2.   
  3.     override fun onCreate(savedInstanceState: Bundle?) {  
  4.         super.onCreate(savedInstanceState)  
  5.         setContentView(R.layout.activity_main)  
  6.   
  7.         viewPager.adapter = MyPager(supportFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)  
  8.     }  
  9. }  
この場合、ログは次のようになります。
  1. System.out: 0 : onStart  
  2. System.out: 1 : onStart  
  3. 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 にしてみます。
  1. class MainActivity : AppCompatActivity() {  
  2.   
  3.     override fun onCreate(savedInstanceState: Bundle?) {  
  4.         super.onCreate(savedInstanceState)  
  5.         setContentView(R.layout.activity_main)  
  6.   
  7.         viewPager.adapter = MyPager(supportFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)  
  8.         viewPager.currentItem = 1  
  9.     }  
  10. }  
この場合のログはこうなります。
  1. System.out: 1 : onStart  
  2. System.out: 0 : onStart  
  3. System.out: 2 : onStart  
  4. 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 をセットしている場合
  1. class MainActivity : AppCompatActivity() {  
  2.   
  3.     override fun onCreate(savedInstanceState: Bundle?) {  
  4.         super.onCreate(savedInstanceState)  
  5.         setContentView(R.layout.activity_main)  
  6.   
  7.         Handler().postDelayed(  
  8.             {   
  9.                 viewPager.adapter = MyPager(supportFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)   
  10.             },  
  11.             1000  
  12.         )  
  13.     }  
  14. }  
onCreate() から1秒後に adapter をセットしてみます。この場合ログは次のようになります。
  1. System.out: 0 : onStart  
  2. System.out: 1 : onStart  
  3. 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 にしてみます。
  1. class MainActivity : AppCompatActivity() {  
  2.   
  3.     override fun onCreate(savedInstanceState: Bundle?) {  
  4.         super.onCreate(savedInstanceState)  
  5.         setContentView(R.layout.activity_main)  
  6.   
  7.         Handler().postDelayed(  
  8.             {  
  9.                 viewPager.adapter = MyPager(supportFragmentManager, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)  
  10.                 viewPager.currentItem = 1  
  11.             },  
  12.             1000  
  13.         )  
  14.     }  
  15. }  
この場合ログは次のようになります。
  1. System.out: 0 : onStart  
  2. System.out: 1 : onStart  
  3. System.out: 0 : onResume  
  4.   
  5. System.out: 2 : onStart  
  6. System.out: 0 : onPause  
  7. 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 件のコメント:

コメントを投稿