Data binding returning null - android

I might be doing this all wrong, but I have the same exact implementation in another fragment/viewmodel with no problems. Maybe because it's a dialog? Every time I log message or message.messagebody it returns null. Can anyone maybe point out why? Currently learning mvvm.
xml: (the important bit since it's long)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="user"
type="com.catbellystudio.knodee.models.Users" />
<variable
name="vm"
type="com.catbellystudio.knodee.ui.profile.ProfileViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_margin="10dp"
android:background="#drawable/custom_background_popup"
android:elevation="10dp"
android:orientation="vertical">
<ScrollView
android:id="#+id/popupTextLayout"
android:layout_width="match_parent"
android:layout_height="277dp"
android:layout_marginTop="8dp">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimary"
android:hint="#string/your_message"
android:inputType="textMultiLine"
android:padding="10dp"
android:text="#{vm.message.messageBody}" />
</ScrollView>
</LinearLayout>
</layout>
viewmodel:
class ProfileViewModel(
private val userRepository: UserRepository,
private val messageRepository: MessageRepository
) : ViewModel() {
var message: Message = Message()
var sender: Users? = null
var receiver: Users? = null
var string:String?=null
fun getLoggedInUser() = runBlocking { userRepository.getUser() }
fun onBackPressed(view: View) {
Navigation.findNavController(view).navigateUp()
}
fun postMessage(view:View) {
Coroutines.main {
Log.e("messagevm", message.toString())
}
}
}
fragment:
class MessageFragment : Fragment(), KodeinAware {
private lateinit var viewModel: ProfileViewModel
private lateinit var profileBinding: FragmentProfileBinding
private lateinit var popupBinding: FragmentPopupBinding
override val kodein by kodein()
private val factory: ProfileViewModelFactory by instance()
private lateinit var dialog: Dialog
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
viewModel = ViewModelProviders.of(this, factory).get(ProfileViewModel::class.java)
profileBinding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_profile,
container,
false
)
popupBinding = FragmentPopupBinding.inflate(LayoutInflater.from(context))
dialog = context?.let { Dialog(it) }!!
dialog.setContentView(popupBinding.root)
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
profileBinding.viewModel = viewModel
popupBinding.vm = viewModel
getSender()
return profileBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
settingsButtonProfile.visibility = View.GONE
messageButtonProfile.setOnClickListener {
showPopUp()
}
val receiver by lazy {
arguments?.let { fromBundle(it).user }
}
viewModel.receiver = receiver
}
private fun showPopUp() {
dialog.show()
val switch = dialog.visibilitySwitchPopup
val visibilityTextView = dialog.visibilityTextViewPopup
dialog.closeButtonPopUp?.setOnClickListener {
dialog.dismiss()
}
switch?.setOnClickListener {
val isIconEnabled = switch.isIconEnabled
if (isIconEnabled) {
visibilityTextView?.text = getString(R.string.anonymous_prompt)
} else {
visibilityTextView?.text = getString(R.string.anonymous_warning)
}
switch.switchState()
}
}
private fun getSender() {
viewModel.getLoggedInUser()?.let { viewModel.sender = it }
}
}
Any help would be appreciated!

move this "popupBinding.vm = viewModel" line to onViewCreated() method and also include this line "popupBinding.lifeCycleOwner=this" in same method

Solved by changing
android:text="#{vm.message.messageBody}
to
android:text="#={vm.message.messageBody}

Related

Android espresso does not navigate to other Fragment?

In my project that makes open new fragment with NavigationComponenet when click button. I want to test if fragment open when click button, But it don't work properly. Only it click button and does not open another fragment. So, I can't test if it works. Why it does not navigate?
#RunWith(AndroidJUnit4::class)
class WelcomeFragmentTestDoctor {
val phoneHelper = PhoneHelper
private lateinit var scenario: FragmentScenario<WelcomeFragment>
#Before
fun setup() {
scenario = launchFragmentInContainer(themeResId = R.style.AppTheme)
scenario.moveToState(Lifecycle.State.STARTED)
Intents.init()
}
#After
fun tearDown(){
Intents.release()
}
#Test
fun clickApplyAsADoctor(){
val navController = TestNavHostController(
ApplicationProvider.getApplicationContext())
scenario.onFragment { fragment ->
navController.setGraph(R.navigation.auth_navigation)
Navigation.setViewNavController(fragment.requireView(), navController)
}
onView(withId(R.id.buttonDoctor)).perform(click())
Assert.assertEquals(navController.currentDestination?.id, R.id.action_welcomeFragment_to_doctorRegistrationFragment)
}
}
fragment_doctor_registration.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="#dimen/_14sdp"
android:paddingBottom="#dimen/_14sdp"
app:layout_constraintTop_toTopOf="parent">
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
DoctorRegistrationFragment.kt
class DoctorRegistrationFragment : Fragment() {
private lateinit var mBinding: FragmentDoctorRegistrationBinding
private val mViewModel: DoctorRegistrationViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
mBinding = FragmentDoctorRegistrationBinding.inflate(inflater, container, false)
return mBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
DoctorRegistrationComponent.inject()
with(mBinding) {
backButton.setOnCrashOnClickListener {
findNavController().popBackStack()
}
btnSend.setOnCrashOnClickListener {
mViewModel.onEvent(
DoctorRegistrationInteractions.RegisterStart(fdr_name.text, fdr_surname.text,
fdr_title.text, fdr_diploma.text, fdr_branch.text,
inputLogin.lifEdittext.text.toString(), fdr_email.text, fdr_address.text,
fdr_company.text, fdr_tax.text
))
}
}
with(mViewModel) {
actions.map { it.getContentIfNotHandled() }.onEach(::handleActions).launchIn(viewLifecycleOwner.lifecycleScope)
}
}
private fun handleActions(action: DoctorRegistrationActions) {
when (action) {
is DoctorRegistrationActions.ErrorMessage -> PopupMessage.error(requireActivity(),message = action.message)
DoctorRegistrationActions.Init -> { }
is DoctorRegistrationActions.SuccessMessage -> {
PopupMessage.success(requireActivity(), message = action.message)
findNavController().popBackStack()
}
}
}
}
You need to check the fragment id not the Action id
Assert.assertEquals(navController.currentDestination?.id, R.id.doctorRegistrationFragment)
Assuming that doctorRegistrationFragment is the id of your fragment tag in your nav graph
<fragment
android:id="#+id/doctorRegistrationFragment"
/* rest of attrs */ >

Binding Adapter live data value is always null

For some reason, the second parameter value for both binding Adapters always returns null and I cannot figure out why. I am selecting a plantIndividual from a RecyclerView in the overview fragment and using it to navigate to a details page - individual fragment. Both Fragments share a viewModel.
Here are my BindingAdapters:
#BindingAdapter("listPhotoData")
fun bindPlantRecyclerView(recyclerView: RecyclerView,
data: List<PlantPhoto>?) {
val adapter = recyclerView.adapter as CollectionIndividualAdapter
adapter.submitList(data)
}
#BindingAdapter("singleImage")
fun loadImage(imgView: ImageView, imgUrl: File) {
imgUrl.let {
Glide.with(imgView.context)
.load(imgUrl)
.apply(
RequestOptions()
.placeholder(R.drawable.loading_animation)
.error(R.drawable.ic_broken_image))
.into(imgView)
}
}
My details fragment layout:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.example.collection.presentation.overview.CollectionOverviewViewModel" />
<variable
name="plantPhoto"
type="com.example.storage.data.PlantPhoto" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">
<ImageView
android:id="#+id/collection_individual_imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:singleImage="#={viewModel.plantPhotoDisplay.plantFilePath}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.26999998"
tools:srcCompat="#tools:sample/avatars" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/collection_individual_recyclerview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/collection_individual_imageview"
app:layout_constraintVertical_bias="0.498"
app:listPhotoData="#={viewModel.listPlantPhoto}"
tools:listitem="#layout/image_plant_photo_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
ViewModel:
class CollectionOverviewViewModel(application: Application) : AndroidViewModel(application) {
lateinit var mediaPlantList: MutableList<File>
private var newPhotoList = mutableListOf<PlantPhoto>()
private val context = getApplication<Application>().applicationContext
private val _navigateToSelectedPlant = MutableLiveData<PlantIndividual>()
val navigateToSelectedPlant: LiveData<PlantIndividual>
get() = _navigateToSelectedPlant
private val _listPlantPhoto = MutableLiveData<MutableList<PlantPhoto>>()
val listPlantPhoto: LiveData<MutableList<PlantPhoto>>
get() = _listPlantPhoto
private val _plantPhotoDisplay = MutableLiveData<PlantPhoto>()
val plantPhotoDisplay: LiveData<PlantPhoto>
get() = _plantPhotoDisplay
fun displayPlantDetails(plantIndividual: PlantIndividual) {
_navigateToSelectedPlant.value = plantIndividual
}
fun displayPlantDetailsComplete() {
_navigateToSelectedPlant.value = null
}
fun retrievePlantList(plantIndividual: PlantIndividual) {
val dataClassNum = plantIndividual.plantId
viewModelScope.launch {
mediaPlantList = context?.getExternalFilesDir("planio/dataclasses/$dataClassNum")
?.listFiles()?.sortedDescending()?.toMutableList() ?: mutableListOf()
}
}
fun changeToPlantPhotos(plantList: MutableList<File>) {
plantList.map {
val file = FileInputStream(it)
val inStream = ObjectInputStream(file)
val item = inStream.readObject() as PlantPhoto
newPhotoList.add(item)
}
_plantPhotoDisplay.value = newPhotoList.last()
_listPlantPhoto.value = newPhotoList
}
}
OverView Fragment from which I am selecting a plantIndividual from a RecyclerView and navigating to a details page:
viewModel.navigateToSelectedPlant.observe(viewLifecycleOwner, {
if (null != it) {
viewModel.retrievePlantList(it)
viewModel.changeToPlantPhotos(viewModel.mediaPlantList)
this.findNavController().navigate(
CollectionOverviewFragmentDirections.
actionCollectionOverviewFragmentToCollectionIndividualFragment(it))
}
})
Details Fragment:
class CollectionIndividualFragment: Fragment() {
private lateinit var binding: FragmentCollectionIndividualBinding
private val viewModel: CollectionOverviewViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_collection_individual, container,
false)
binding.toCollectionOverview.setOnClickListener {
this.findNavController().navigate(CollectionIndividualFragmentDirections.
actionCollectionIndividualFragmentToCollectionOverviewFragment())
viewModel.displayPlantDetailsComplete()
}
binding.viewModel = viewModel
binding.lifecycleOwner = this
binding.collectionIndividualRecyclerview.adapter = CollectionIndividualAdapter()
binding.plantPhoto = viewModel.plantPhotoDisplay.value
return binding.root
}
I guess you're setting the lifecycleOwner after setting the viewModel of your binding and as a result, after viewModel is set in binding, it cannot observe live data because the lifecycleOwner is null at that point. I suggest to set it after setting binding
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_collection_individual, container,
false)
binding.lifecycleOwner = this
Edit:
Also don't forget to use by activityViewModels instead of by viewModels to share viewModel among your fragments

Android: LiveData postValue() getting null

I am trying to transfer a value from one LiveData (Repository.getMovieList(editTextContent.value.toString()).value) to another LiveData (this.movieList.postValue) using postValue().
I am observing the movieList and want to change it's value from the Repo depending on different buttons that were clicked but I when it runs, it only gets the null value and doesn't wait till the Repo's LiveData gets their value.
Fragment xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewmodel"
type="com.example.movieapp.ui.search.SearchMovieFragmentViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.search.SearchMovieFragment">
<EditText
android:id="#+id/search_movie_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#={viewmodel.editTextContent}"
android:inputType="text"
android:hint="Movie Name" />
<Button
android:id="#+id/search_fragment_search_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Search"
android:onClick="#{() -> viewmodel.getMovieSearchList()}"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/search_movie_fragment_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</layout>
SearchMovieFragment
class SearchMovieFragment : Fragment(), MovieSearchItemViewModel {
companion object {
fun newInstance() = SearchMovieFragment()
}
private lateinit var searchMovieFragmentViewModel: SearchMovieFragmentViewModel
private lateinit var binding: SearchMovieFragmentBinding
private lateinit var movieRecyclerView: RecyclerView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.search_movie_fragment, container, false)
searchMovieFragmentViewModel = ViewModelProvider(this).get(SearchMovieFragmentViewModel::class.java)
binding.lifecycleOwner = this
binding.viewmodel = searchMovieFragmentViewModel
setUpRecyclerView(container!!.context)
return binding.root
}
private fun setUpRecyclerView(context: Context) {
movieRecyclerView = binding.searchMovieFragmentRecyclerView.apply {
this.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
}
val adapter = MovieListAdapter()
adapter.setCallback(this)
binding.searchMovieFragmentRecyclerView.adapter = adapter
searchMovieFragmentViewModel.getMovieListLiveData().observe(viewLifecycleOwner, Observer {movieList ->
adapter.submitList(movieList)
})
}
}
SearchMovieViewModel
class SearchMovieFragmentViewModel : ViewModel() {
val editTextContent = MutableLiveData<String>()
var movieList: MutableLiveData<List<Movie>> = MutableLiveData()
fun getMovieSearchList(){
this.movieList.postValue(Repository.getMovieList(editTextContent.value.toString()).value)
}
fun getTrendingMovies() {
movieList.postValue(Repository.getTrendingMovies().value)
}
fun getMovieDetail(movieId: String): MutableLiveData<Movie> {
return Repository.getMovieDetail(movieId)
}
fun getMovieListLiveData() : LiveData<List<Movie>> {
return movieList
}
private fun getMovieList(movieSearch: String): MutableLiveData<List<Movie>> = Repository.getMovieList(movieSearch)
}
I think you are implementing it the wrong way, Instead, using the MediatorLiveData will be a good and practical solution as it allows you to observe multiple LiveData objects and select between them based on your preferences (a specific action for example).
This is an example of how to implement it in your case
val editTextContent = MutableLiveData<String>()
val finalList = MediatorLiveData<List<Movie>>()
// Here.. Define all of your LiveData objects
private val movieList = repository.getMovieList(editTextContent.value.toString())
private val trendingMovies = repository.getTrendingMovies()
private val movieDetail = repository.getMovieDetail()
fun setSelection(selection: String) {
finalList.addSource(movieList) { result ->
if (selection == "movieList") {
result?.let { finalList.value = it }
}
}
finalList.addSource(trendingMovies) { result ->
if (selection == "trendingMovies") {
result?.let { finalList.value = it }
}
}
finalList.addSource(movieDetail) { result ->
if (selection == "movieDetail") {
result?.let { finalList.value = it }
}
}
}
So what you have to do is to only observe the MediatorLiveData and then call the setSelection function and send the correspondent selection action to it as a parameter and it will switch the observation to another LiveData

How to open a fragment from another fragment using MVVM

I have a fragment ProductsFragment in which I have a button AddProduct when it is clicked I want to open a different fragment AddProductFragment.
I am using MVVM architecture
I went through this link and done the below mentioned implementation, but I did not quite understand or did not mention where fragment I want to navigate to
Error message
ProductsFragment - THE ISSUE IS HERE IN ONVIEWCREATED METHOD*
class ProductsFragment: Fragment() {
private lateinit var binding: ProductsBinding
private lateinit var navController: NavController
private lateinit var productsViewModel: ProductsViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.products, container, false)
val dao = SubscriberDatabase.getInstance(activity!!.applicationContext).productDAO
val repository = ProductRepository(dao)
val factory = ProductsViewModelFactory(repository, activity!!.applicationContext)
productsViewModel = ViewModelProvider(this, factory).get(ProductsViewModel::class.java)
binding.productsViewModel = productsViewModel
binding.lifecycleOwner = this
val view = binding.root
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navController = Navigation.findNavController(view)
productsViewModel.navigateScreen.observe(activity!!, EventObserver {
navController.navigate(it) //issues is here
})
}
}
Products
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<data class=".ProductsBinding">
<variable
name="productsViewModel"
type="com.rao.iremind.ProductsViewModel" />
</data>
<LinearLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Testing text"/>
<Button
android:id="#+id/btn_add_product"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add product"
android:onClick="#{() -> productsViewModel.addProduct()}"/>
<View
android:id="#+id/frgSpace"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</layout>
ProductViewModel
class ProductsViewModel (
private val repository: ProductRepository,
private val context: Context
): ViewModel() {
private val _navigateScreen = MutableLiveData<Event<Any>>()
val navigateScreen: LiveData<Event<Any>> = _navigateScreen
fun addProduct() {
Toast.makeText(context, "Products view model", Toast.LENGTH_LONG).show()
_navigateScreen.value = Event(R.id.frgSpace)
}
}
Event
open class Event<out T>(private val content: T) {
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
class EventObserver<Int>(private val onEventUnhandledContent: (Int) -> Unit) : Observer<Event<Int>> {
override fun onChanged(event: Event<Int>?) {
event?.getContentIfNotHandled()?.let {
onEventUnhandledContent(it)
}
}
}
ProductsViewModelFactory
class ProductsViewModelFactory (
private val repository: ProductRepository,
private val context: Context
) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(ProductsViewModel::class.java)) {
return ProductsViewModel(repository, context) as T
}
throw IllegalArgumentException("Unknown View Model class")
}
}
I want to navigate to this fragment
class AddProductFragment: Fragment() {
private lateinit var binding: AddProductBinding
private lateinit var addProductViewModel: AddProductViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.add_product, container, false)
val dao = SubscriberDatabase.getInstance(activity!!.applicationContext).productDAO
val repository = ProductRepository(dao)
val factory = ProductsViewModelFactory(repository, activity!!.applicationContext)
addProductViewModel = ViewModelProvider(this, factory).get(AddProductViewModel::class.java)
binding.addProductViewModel = addProductViewModel
binding.lifecycleOwner = this
val view = binding.root
return view
}
}
Thanks
R
It seems that your EventObserver class is expecting an Int but you are sending Any in LiveData<Event<Any>>
Try changing
private val _navigateScreen = MutableLiveData<Event<Any>>()
val navigateScreen: LiveData<Event<Any>> = _navigateScreen
to
private val _navigateScreen = MutableLiveData<Event<Int>>()
val navigateScreen: LiveData<Event<Int>> = _navigateScreen
I would also recommend you to replace activity!! with viewLifecycleOwner in this line:
productsViewModel.navigateScreen.observe(viewLifecycleOwner, EventObserver {...})
so that your fragment does not receive any LiveData updates when its view is destroyed.

DialogFragment with view model not working with data binding

I've created one dialog fragemnt with view model (mvvm). Dialog consist of one button (custom view). when using view model with data binding, button click is not working when livedata change.I'm using boolean value to check if button is clicked or not. What is causing issue? Also suggest any other approach if needed.
profile_dialog_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewmodel"
type="com.test.ui.ProfileDialogViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.ProfileDialog">
<com.google.android.material.button.MaterialButton
android:id="#+id/login"
style="#style/TextAppearance.MaterialComponents.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Login"
android:onClick="#{() -> viewmodel.onLoginButtonClick()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
ProfileDialog.kt
class ProfileDialog : DialogFragment() {
companion object {
fun newInstance() = ProfileDialog()
}
private val viewModel: ProfileDialogViewModel by viewModel()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = ProfileDialogFragmentBinding.inflate(inflater, container, false)
.apply {
this.lifecycleOwner = this#ProfileDialog
this.viewmodel = viewmodel
}
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel.startLogin.observe(viewLifecycleOwner, Observer {
Log.d("insta", "This is working")
if (it == null) return#Observer
if(it) {
Log.d("insta", "This is not working")
val loginIntent = Intent(this.context, LoginActivity::class.java)
this.context?.startActivity(loginIntent)
}
})
}
}
ProfileDialogViewModel.kt
class ProfileDialogViewModel : ViewModel() {
private val _startLogin = MutableLiveData<Boolean>(false)
val startLogin: LiveData<Boolean>
get() = _startLogin
fun onLoginButtonClick() {
Log.d("insta", "This ain't working")
_startLogin.postValue(true)
}
}
Your viewmodel is defined in
private val viewModel: ProfileDialogViewModel by viewModel()
So, pay attention to viewModel. The problem located in
this.viewmodel = viewmodel
where this points to ProfileDialogFragmentBinding. Here you assinging ProfileDialogFragmentBinding.viewmodel = ProfileDialogFragmentBinding.viewmodel - that's why it's not working.
To solve problem, properly assign it like that:
this.viewmodel = viewModel

Categories

Resources