Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Single Activity with MVVM

Single Activity with MVVM

TakuSemba

March 26, 2019
Tweet

More Decks by TakuSemba

Other Decks in Technology

Transcript

  1. TakuSemba
    CyberAgent.Inc
    Single Activity
    with MVVM

    View Slide

  2. TakuSemba
    CyberAgent.Inc
    Single Activity
    with MVVM

    View Slide

  3. @takusemba
    https://github.com/TakuSemba

    View Slide

  4. https://developer.android.com/jetpack/docs/guide
    MVVM

    View Slide

  5. Navigation
    https://developer.android.com/guide/navigation/navigation-getting-started

    View Slide


  6. xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph">

    View Slide


  7. xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph">
    android:name=“your.package.FirstFragment” />
    android:name=“your.package.SecondFragment”/>

    android:name=“your.package.FirstFragment" />
    android:name="your.package.SecondFragment"/>

    View Slide


  8. xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/firstFragment">
    android:name=“your.package.FirstFragment” />
    android:name=“your.package.SecondFragment”/>

    app:startDestination="@id/firstFragment">
    android:name=“your.package.FirstFragment" />
    android:name="your.package.SecondFragment"/>

    View Slide


  9. xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/firstFragment">
    android:name=“your.package.FirstFragment">
    app:destination="@id/secondFragment"/>

    android:name=“your.package.SecondFragment"/>

    android:name="your.package.FirstFragment">
    app:destination="@id/secondFragment"/>

    View Slide


  10. xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/firstFragment">
    android:name="your.package.FirstFragment">
    app:destination="@id/secondFragment"/>

    android:name=“your.package.SecondFragment">



    android:name=“your.package.SecondFragment">


    View Slide


  11. xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/firstFragment">
    android:name="your.package.FirstFragment">
    app:destination="@id/secondFragment"/>

    android:name=“your.package.SecondFragment">



    View Slide

  12. View Slide

  13. val navController = findNavController(R.id.host_fragment)
    // find from activity

    View Slide

  14. val navController = findNavController(R.id.host_fragment)
    // find from activity
    val navController = findNavController()
    // find from fragment

    View Slide

  15. val navController = findNavController(R.id.host_fragment)
    // find from activity
    val navController = findNavController()
    // find from fragment
    val navController = findNavController()
    // find from view

    View Slide

  16. val navController = findNavController(R.id.host_fragment)
    // find from activity
    val navController = findNavController()
    // find from fragment
    val navController = findNavController()
    // find from view
    val args = bundleOf(SecondFragment.KEY_USER_ID to userId)
    navController.navigate(R.id.toSecondFragment, args)
    // navigate to secondFragment.kt

    View Slide

  17. val navController = findNavController(R.id.host_fragment)
    // find from activity
    val navController = findNavController()
    // find from fragment
    val navController = findNavController()
    // find from view
    // navigate to secondFragment.kt
    val directions = FirstFragmentDirections.toSecondFragment(userId)
    navController.navigate(directions)

    View Slide

  18. val navController = findNavController(R.id.host_fragment)
    // find from activity
    val navController = findNavController()
    // find from fragment
    val navController = findNavController()
    // find from view
    // navigate to secondFragment.kt
    val directions = FirstFragmentDirections.toSecondFragment(userId)
    navController.navigate(directions)
    // get userId from arguments
    val args = SecondFragmentArgs.fromBundle(requireArguments())
    val userId = args.userId

    View Slide

  19. val navController = findNavController(R.id.host_fragment)
    // find from activity
    val navController = findNavController()
    // find from fragment
    val navController = findNavController()
    // find from view
    // navigate to secondFragment.kt
    val directions = FirstFragmentDirections.toSecondFragment(userId)
    navController.navigate(directions)
    // get userId from arguments
    private val args by navArgs()
    val userId = args.userId

    View Slide

  20. Global Action
    a common action that multiple destinations can use

    View Slide

  21. Global Action
    a common action that multiple destinations can use
    ɾMaintenance
    ɾForceUpdate
    ɾError

    View Slide

  22. // global action

    View Slide


  23. xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@id/launcherFragment"
    >
    android:id="@+id/action_global_ maintainanceFragment”
    app:destination="@id/maintainanceFragment"
    />



    // global action

    View Slide

  24. // navigate to maintainanceFragment using GlobalAction
    val directions = NavGraphDirections.actionGlobalMaintainanceFragment()

    View Slide

  25. // navigate to maintainanceFragment using GlobalAction
    val directions = NavGraphDirections.actionGlobalMaintainanceFragment()
    navController.navigate(directions)

    View Slide

  26. Nav Options
    special options for navigate actions
    public NavOptions.Builder setPopUpTo (int destinationId, boolean inclusive)
    public NavOptions.Builder setLaunchSingleTop (boolean singleTop)

    View Slide

  27. // navigate with stacked fragmetns popoed up

    View Slide

  28. val directions = SplashDirections.showLoginFragment()
    navController.navigate(directions)
    // navigate with stacked fragmetns popoed up

    View Slide

  29. val directions = SplashDirections.showLoginFragment()
    val options = NavOptions.Builder()
    .setPopUpTo(R.id.splashFragment, true)
    .build()
    navController.navigate(directions, options)
    // navigate with stacked fragmetns popoed up

    View Slide

  30. // navigate to loginFragment
    val directions = SplashDirections.showLoginFragment()
    navController.navigate(directions)


    android:name="your.package.SplashFragment"
    tools:layout="@layout/fragment_splash">
    app:destination="@id/loginFragment"
    app:popUpTo="@id/splashFragment"
    app:popUpToInclusive="true"/>


    app:popUpTo="@id/splashFragment"
    app:popUpToInclusive="true"/>
    // navigate with stacked fragmetns popoed up

    View Slide

  31. // navigate as single-top
    val directions = NavGraphDirections.showBarFragment()
    navController.navigate(directions)
    val directionsAgain = NavGraphDirections.showBarFragment()
    navController.navigate(directionsAgain)

    View Slide

  32. val directions = NavGraphDirections.showBarFragment()
    navController.navigate(directions)
    val directionsAgain = NavGraphDirections.showBarFragment()
    val options = NavOptions.Builder()
    .setLaunchSingleTop(true)
    .build()
    navController.navigate(directions, options)
    // navigate as single-top

    View Slide

  33. // navigate to barFragment
    val directions = NavGraphDirections.showBarFragment()
    navController.navigate(directions)


    android:name="your.package.SplashFragment"
    tools:layout="@layout/fragment_splash">
    app:destination="@id/loginFragment"
    app:launchSingleTop="true"/>


    app:launchSingleTop="true"/>
    // navigate as single-top

    View Slide

  34. // navigate back

    View Slide

  35. navController.popBackStack()
    // navigate back

    View Slide

  36. navController.popBackStack()
    // navigate back
    // observe data
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel.user.observe(this) { user ->
    textView.text = user.name
    }
    }

    View Slide

  37. navController.popBackStack()
    // navigate back
    // observe data
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel.user.observe(this) { user ->
    textView.text = user.name
    }
    }
    observe(this) { user ->
    }

    View Slide

  38. navController.popBackStack()
    // navigate back
    // observe data
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel.user.observe(this) { user ->
    textView.text = user.name
    }
    }
    observe(this) { user ->
    }
    override fun onDestroy() {
    super.onDestroy()
    // not called yet
    }

    View Slide

  39. navController.popBackStack()
    // navigate back
    // observe data
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel.user.observe(viewLifecycleOwner) { user ->
    textView.text = user.name
    }
    }
    override fun onDestroy() {
    super.onDestroy()
    // not called yet
    }
    observe(viewLifecycleOwner) { user ->
    }

    View Slide

  40. navController.popBackStack()
    // navigate back
    // observe data
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel.user.observe(viewLifecycleOwner) { user ->
    textView.text = user.name
    }
    }
    override fun onDestroyView() {
    super.onDestroyView()
    // observer is removed.
    }
    observe(viewLifecycleOwner) { user ->
    }

    View Slide

  41. IUUQTNFEJVNDPN!UBLVTFNCB

    View Slide

  42. class FooViewModel : ViewModel() {
    val snackbarMessage: LiveData = MutableLiveData()
    }
    // viewModel

    View Slide

  43. class FooViewModel : ViewModel() {
    val snackbarMessage: LiveData = MutableLiveData()
    }
    // viewModel
    // observe data
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel.snackbarMessage.observe(viewLifecycleOwner) { message ->
    // show snackbar
    }
    }

    View Slide

  44. class FooViewModel : ViewModel() {
    val snackbarMessage: LiveData> = MutableLiveData()
    }
    // viewModel
    // observe data
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel.snackbarMessage.observe(viewLifecycleOwner) { message ->
    // show snackbar
    }
    }

    View Slide

  45. class FooViewModel : ViewModel() {
    val snackbarMessage: LiveData> = MutableLiveData()
    }
    // viewModel
    // observe data
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel.snackbarMessage.observe(viewLifecycleOwner) { message ->
    // show snackbar
    }
    }
    // Event.kt from https://github.com/google/iosched
    LiveData>

    View Slide

  46. class FooViewModel : ViewModel() {
    // Event.kt from https://github.com/google/iosched
    val snackbarMessage: LiveData> = MutableLiveData()
    }
    // viewModel
    // observe data
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel.snackbarMessage.observe(viewLifecycleOwner) { event ->
    event.getContentIfNotHandled()?.let { message ->
    // show snackbar
    }
    }
    }

    View Slide

  47. IUUQTNFEJVNDPNBOESPJEEFWFMPQFSTMJWFEBUBXJUITOBDLCBSOBWJHBUJPOBOEPUIFSFWFOUTUIF
    TJOHMFMJWFFWFOUDBTFBD

    View Slide

  48. // scope for viewModel
    val activityScopedViewModel = ViewModelProviders
    .of(requireActivity(), viewModelFactory)
    .get(FooViewModel::class.java)

    View Slide

  49. // scope for viewModel
    private val activityScopedViewModel: FooViewModel by activityViewModels {
    viewModelFactory
    }

    View Slide

  50. // scope for viewModel
    private val activityScopedViewModel: FooViewModel by activityViewModels {
    viewModelFactory
    }
    Activity Scope

    View Slide

  51. // scope for viewModel
    private val activityScopedViewModel: FooViewModel by activityViewModels {
    viewModelFactory
    }
    Activity Scope ≒ App Scope

    View Slide

  52. // scope for viewModel
    private val activityScopedViewModel: FooViewModel by activityViewModels {
    viewModelFactory
    }
    private val fragmentScopedViewModel: BarViewModel by viewModels {
    viewModelFactory
    }
    Activity Scope ≒ App Scope

    View Slide

  53. // scope for viewModel
    private val activityScopedViewModel: FooViewModel by activityViewModels {
    viewModelFactory
    }
    private val fragmentScopedViewModel: BarViewModel by viewModels {
    viewModelFactory
    }
    Activity Scope ≒ App Scope

    Fragment Scope Fragment Scope

    View Slide

  54. https://www.youtube.com/watch?v=2k8x8V77CrU&t=1300s

    View Slide

  55. https://github.com/takusemba
    https://twitter.com/takusemba
    Single Activity
    with MVVM

    View Slide