Home > Back-end >  Handle search results after configuration change MVVM
Handle search results after configuration change MVVM

Time:12-22

I am building a movie app based on "The Movie DB". My app has only one activity with a recyclerview and one search action button (SearchView). As soon as the app starts,the recyclerview filled with popularMovies list.

When I search for a movie I get the results and everything is fine,but when I rotate the screen I get the popularMovies list and not my search results.

Now I know the reason,its happening because in onCreate I have initMoviesRecyclerView() that fill the recyclerview with popularMovies.

My question is how can I keep the search results without filling the recyclerview with popularMovies ? How can I do this the correct way ?

This is the Repository Class:

class MainRepository {

    //MutableLiveData
    private val popularMoviesMutableLiveData = MutableLiveData<List<Movie>>()
    private val searchAfterMutableLiveData = MutableLiveData<List<Movie>>()

    // API
    private val apiService : GetFromApi = APIService.retrofitClientRequest
    private val apiKey = "NOT"

     
    fun getPopularMoviesList() : MutableLiveData<List<Movie>>{

        apiService.getPopularMovies(apiKey,1)?.enqueue(object : Callback<MovieListResult?> {
            override fun onResponse(
                call: Call<MovieListResult?>,
                response: Response<MovieListResult?>
            ) {

                if (response.isSuccessful){

                    popularMoviesMutableLiveData.value = response.body()?.moviesResults
                    Log.e("MovieListResults","Result: ${popularMoviesMutableLiveData.value}")
                }

            }

            override fun onFailure(call: Call<MovieListResult?>, t: Throwable) { 
                Log.e("MovieListResults","Failed: ${t.message}")
            }
        })

        return popularMoviesMutableLiveData

    }

    fun searchAfter(searchAfter : String) : MutableLiveData<List<Movie>>{

        apiService.searchAfter(apiKey,searchAfter)?.enqueue(object : Callback<MovieListResult?> {
            override fun onResponse(
                call: Call<MovieListResult?>,
                response: Response<MovieListResult?>
            ) {

                if (response.isSuccessful){
                    searchAfterMutableLiveData.value = response.body()?.moviesResults
                    Log.e("SearchMovieListResults","Result: ${searchAfterMutableLiveData.value}")
                }

            }

            override fun onFailure(call: Call<MovieListResult?>, t: Throwable) {
                Log.e("SearchMovieListResults","Failed: ${t.message}")
            }
        })

        return searchAfterMutableLiveData
    }

}

This is the viewModel class:

class MainViewModel : ViewModel(){

    //Repository
    private val mainRepository = MainRepository()

    //MutableLiveData
    var popularMoviesMutableLiveData = MutableLiveData<List<Movie>>()
    var searchAfterMutableLiveData = MutableLiveData<List<Movie>>()

    //The Main Movie List
    var mainMovieList = listOf<Movie>()

    fun getPopularMovies() : LiveData<List<Movie>>{
        popularMoviesMutableLiveData = mainRepository.getPopularMoviesList()
        popularMoviesMutableLiveData.value = mainMovieList
        return popularMoviesMutableLiveData
    }

    fun getMovieBySearch(searchAfter : String) : LiveData<List<Movie>>{
        searchAfterMutableLiveData = mainRepository.searchAfter(searchAfter)
        searchAfterMutableLiveData.value = mainMovieList
        return searchAfterMutableLiveData
    }


}

This is the MainActivity class:

class MainActivity : AppCompatActivity() {



    //ViewModel
    private val mainViewModel : MainViewModel by viewModels()

    // Views
    private lateinit var mainRecyclerView : RecyclerView
    private lateinit var mainAdapter : MainRecyclerViewAdapter

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

        
        initMoviesRecyclerView()


    }


    private fun initMoviesRecyclerView() {

        mainRecyclerView = findViewById(R.id.mainRecyclerView)
        mainRecyclerView.setHasFixedSize(true)
        mainRecyclerView.layoutManager = GridLayoutManager(this,1)

        mainViewModel.getPopularMovies().observe(this, object : Observer<List<Movie>?> {
            override fun onChanged(newList: List<Movie>?) {

                if (newList != null) {

                    mainViewModel.mainMovieList = newList
                    mainAdapter = MainRecyclerViewAdapter(mainViewModel.mainMovieList)
                    mainRecyclerView.adapter = mainAdapter

                }

            }
        })

    }




    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.main_menu,menu)

        val searchView = menu.findItem(R.id.menu_search_movie).actionView as androidx.appcompat.widget.SearchView

        searchView.queryHint = "Search By Name,Actor .."
        searchView.setOnQueryTextListener(object : androidx.appcompat.widget.SearchView.OnQueryTextListener {
            override fun onQueryTextSubmit(whileTextChange: String?): Boolean {
                //Clear SearchView
                searchView.isIconified = true
                searchView.setQuery("", false)
                searchView.onActionViewCollapsed()
                mainViewModel.getMovieBySearch(whileTextChange.toString()).observe(this@MainActivity,object : Observer<List<Movie>?> {
                    override fun onChanged(newList: List<Movie>?) {
                        if (newList != null) {

                            mainViewModel.mainMovieList = newList
                            mainAdapter.changeCurrentList(mainViewModel.mainMovieList)
                        }
                    }
                })

                return false
            }

            override fun onQueryTextChange(whileTextChange: String?): Boolean {
                Log.e("onQueryTextChange","Text: $whileTextChange")
                return false
            }
        })

        return true
    }



}

Thank you!

CodePudding user response:

I don't see where you actually populate the data inside the ViewModel. But it all looked good.

This line is a bit suspicious:

mainViewModel.mainMovieList = newList

You should not update the ViewModel from the View lifeCycle. Also, you should not update the ViewModel during the observe call.

CodePudding user response:

So after sometime I manage to work around it with a simple solution:

I created a function in the viewModel,to check if the search list value is null:

fun startFromSearch(): Boolean {
    return searchAfterMutableLiveData.value != null
}

After that I made an if statement in the initMoviesRecyclerView(), and that's it!

    private fun initMoviesRecyclerView() {
    
        mainRecyclerView = findViewById(R.id.mainRecyclerView)
        mainRecyclerView.setHasFixedSize(true)
        mainRecyclerView.layoutManager = GridLayoutManager(this,1)
        mainAdapter = MainRecyclerViewAdapter(mainViewModel.mainMovieList,this)
        mainRecyclerView.adapter = mainAdapter
    
    
    
        if (!mainViewModel.startFromSearch()){
    
            mainViewModel.getPopularMovies(1).observe(this, object : Observer<List<Movie>?> {
                override fun onChanged(newList: List<Movie>?) {
    
                    if (newList != null) {
    
                        mainAdapter = MainRecyclerViewAdapter(newList,this@MainActivity)
                        mainRecyclerView.adapter = mainAdapter
    
                    }
    
                }
            })
        }
}
  • Related