简体   繁体   中英

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM