https://stackoverflow.com/a/57630353/3151712

질문) ViewModelProviders is deprecated So what’s the alternative to create the ViewModel’s object ?

답) 

boardViewModel = ViewModelProvider(this).get(BoardViewModel::class.java)

답)

private val viewModel: EntityGridViewModel by viewModels()

위의 방법으로 사용했지만 constructor가 있는 ViewModel의 경우 위의 구문실행시기가 이른 시기에 해당해서 constructor에 전달될 parameter를 준비하는데 시간이 부족한 경우가 많다. 그래서 다른 방법을 사용하게 되었다. 

https://www.albertgao.xyz/2018/04/13/how-to-add-additional-parameters-to-viewmodel-via-kotlin/

binding.authViewModel = ViewModelProviders.of(
 this,
 viewModelFactory { MyViewModel("albert") }
).get(AuthViewModel::class.java)

참고자료) passing parameters viewmodel

https://stackoverflow.com/a/60130631/3151712

https://www.albertgao.xyz/2018/04/13/how-to-add-additional-parameters-to-viewmodel-via-kotlin/

.

.

.

https://developer.android.com/topic/libraries/architecture/viewmodel

ViewModel 전반적인 사용 방법

.

.

위와 같이 사용하려고 하였으나 자동완성이 되지 않았다. dependency 설정이 잘못되어있었기 때문이다. 

    // ViewModel
//    android docs ref) https://developer.android.com/jetpack/androidx/releases/lifecycle
   def lifecycle_version = "2.2.0"
   def arch_version = "2.1.0"
   implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
   // LiveData
   implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"

//    ref) https://developer.android.com/kotlin/ktx#fragment
   implementation "androidx.fragment:fragment-ktx:1.2.5"

   //ViewModel
//    from codelab ref) https://codelabs.developers.google.com/codelabs/kotlin-android-training-view-model/index.html?index=..%2F..android-kotlin-fundamentals#4
   implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'

가 필요했다. 

https://developer.android.com/topic/libraries/architecture/viewmodel#sharing

https://developer.android.com/kotlin/ktx#fragment

implementation "androidx.fragment:fragment-ktx:1.2.5"

.

.

.

.

주의사항)

https://developer.android.com/topic/libraries/architecture/viewmodel

Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.

image

.

.

.

.

하나의 activity 아래에 여러개의 fragment가 있고 activity lifecycle에 맞춰서 share view model을 사용하는 방법 

기본적으로 아래 내용은 그대로 이나 약간 문법이 바뀌었다. 

변화된 내용은 https://developer.android.com/topic/libraries/architecture/viewmodel#sharing 에서 확인할수 있다. 변화된 코드는 아래와 같다. 먼저 바로 아래 것을 보고 밑에것을 보면 된다. 

class SharedViewModel : ViewModel() {
   val selected = MutableLiveData<Item>()

   fun select(item: Item) {
       selected.value = item
   }
}

class MasterFragment : Fragment() {

   private lateinit var itemSelector: Selector

   // Use the 'by activityViewModels()' Kotlin property delegate
   // from the fragment-ktx artifact
   private val model: SharedViewModel by activityViewModels()

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
       super.onViewCreated(view, savedInstanceState)
       itemSelector.setOnClickListener { item ->
           // Update the UI
       }
   }
}

class DetailFragment : Fragment() {

   // Use the 'by activityViewModels()' Kotlin property delegate
   // from the fragment-ktx artifact
   private val model: SharedViewModel by activityViewModels()

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
       super.onViewCreated(view, savedInstanceState)
       model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
           // Update the UI
       })
   }
}

.

https://medium.com/mindorks/how-to-communicate-between-fragments-and-activity-using-viewmodel-ca733233a51c

Here comes ViewModel to rescue us from handling a lot of scenario and implementing interface . We only need to create ViewModel class and create instance in fragment but using the activity scope so that it will be available for all the fragment of the activity including activity itself.

Create a ViewModel class

class SharedViewModel:ViewModel(){
   val inputNumber = MutableLiveData<Int>()
}

To emit or pass data from our input fragment create ViewModel in activity scope. To do this we have to pass the activity reference as argument of the ViewModelProvides.of() method. Noe just pass the data to ViewModel object like this

activity?.let {
   sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)
}

et_input.addTextChangedListener(object : TextWatcher {
   override fun afterTextChanged(p0: Editable?) {}

   override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}

   override fun onTextChanged(txt: CharSequence?, p1: Int, p2: Int, p3: Int) {
       txt?.let {
           var input = 0
           if (txt.toString().isNotEmpty()) {
               input = txt.toString().toInt()
           }

           sharedViewModel?.inputNumber?.postValue(input)
       }
   }

In the activity we just need to create instance of our ViewModel and observe the required data like this

val sharedViewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)

sharedViewModel.inputNumber.observe(this, Observer {
   it?.let {
       // do some thing with the number
   }
})

Now what about output fragment? We can do same for the output fragment to observe the data. But keep in mind that we need create the ViewModel instance in activity scope, otherwise android will create a separate instance rather than sharing the same instance and we will not get the data.

For output fragment do it like this

activity?.let {
   val sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)

   sharedViewModel.inputNumber.observe(this, Observer {
   it?.let {
           // do some thing with the number
       }
   })
}

That’s it. Source code can be found here.

.

source code

class InputFragment : Fragment() {

    private var sharedViewModel: SharedViewModel? = null

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_input, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        activity?.let {
            /**
             *  create view model in activity scope
             */
            sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)
        }

        et_input.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(p0: Editable?) {}

            override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}

            override fun onTextChanged(txt: CharSequence?, p1: Int, p2: Int, p3: Int) {
                txt?.let {
                    var input = 0
                    if (txt.toString().isNotEmpty()) {
                        input = txt.toString().toInt()
                    }

                    sharedViewModel?.inputNumber?.postValue(input)
                }
            }
        })
    }

}
package ninja.shuza.sharedviewmodel

import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import kotlin.properties.Delegates

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        supportFragmentManager.beginTransaction().add(R.id.layout_top, InputFragment()).commit()
        supportFragmentManager.beginTransaction().add(R.id.layout_bottom, OutputFragment()).commit()

        val message = resources.getString(R.string.show_input)

        val sharedViewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)

        sharedViewModel.inputNumber.observe(this, Observer {
            it?.let {
                tv_show_input.text = "$message  $it"
            }
        })
    }
}
package ninja.shuza.sharedviewmodel

import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_output.*

/**
 *
 * :=  created by:  Shuza
 * :=  create date:  09-Aug-2018
 * :=  (C) CopyRight Shuza
 * :=  www.shuza.ninja
 * :=  shuza.sa@gmail.com
 * :=  Fun  :  Coffee  :  Code
 *
 **/
class OutputFragment : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_output, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        /**
         *  create view model in activity scope
         */
        activity?.let {
            val sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)

            observeInput(sharedViewModel)
        }
    }

    private fun observeInput(sharedViewModel: SharedViewModel) {
        sharedViewModel.inputNumber.observe(this, Observer {
            it?.let {
                tv_output.text = "2 x $it  =  ${2 * it}"
            }
        })
    }

}

.

.

.

.

share viewmodel with several fragment

original source : https://medium.com/mindorks/how-to-communicate-between-fragments-and-activity-using-viewmodel-ca733233a51c

image

Here comes ViewModel to rescue us from handling a lot of scenario and implementing interface . We only need to create ViewModel class and create instance in fragment but using the activity scope so that it will be available for all the fragment of the activity including activity itself.

Create a ViewModel class

class SharedViewModel:ViewModel(){
   val inputNumber = MutableLiveData<Int>()
}

To emit or pass data from our input fragment create ViewModel in activity scope. To do this we have to pass the activity reference as argument of the ViewModelProvides.of() method. Noe just pass the data to ViewModel object like this

activity?.let {
   sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)
}

et_input.addTextChangedListener(object : TextWatcher {
   override fun afterTextChanged(p0: Editable?) {}

   override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}

   override fun onTextChanged(txt: CharSequence?, p1: Int, p2: Int, p3: Int) {
       txt?.let {
           var input = 0
           if (txt.toString().isNotEmpty()) {
               input = txt.toString().toInt()
           }

           sharedViewModel?.inputNumber?.postValue(input)
       }
   }

In the activity we just need to create instance of our ViewModel and observe the required data like this

val sharedViewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)

sharedViewModel.inputNumber.observe(this, Observer {
   it?.let {
       // do some thing with the number
   }
})

Now what about output fragment? We can do same for the output fragment to observe the data. But keep in mind that we need create the ViewModel instance in activity scope, otherwise android will create a separate instance rather than sharing the same instance and we will not get the data.

For output fragment do it like this

activity?.let {
   val sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)

   sharedViewModel.inputNumber.observe(this, Observer {
   it?.let {
           // do some thing with the number
       }
   })
}

That’s it. Source code can be found here.

Comments are closed.

Post Navigation