更改数据时不会通知观察者

时间:2020-09-07 14:44:45

标签: android android-livedata observers

我有一个活动,该活动具有一个SearchView,可用于输入查询,然后我的应用程序用于查询以访问API。我的活动还包含一个片段,在这个片段中,我有我的观察者。

此外,我还有ViewModel,它可以在查询时调用API。但是,我的观察者永远不会收到有关更新的通知,因此我的视图永远不会更新。除非我在启动时直接从ViewModel调用它。我将在这里专门显示:

ViewModel

class SearchViewModel : ViewModel() {

    val booksResponse = MutableLiveData<MutableList<BookResponse>>()
  
    val loading = MutableLiveData<Boolean>()
    val error = MutableLiveData<String>()


    init {
    getBooks("How to talk to a widower")
    }

    fun getBooks(bookTitle: String) {
        GoogleBooksService.api.getBooks(bookTitle).enqueue(object: Callback<ResponseWrapper<BookResponse>> {

            override fun onFailure(call: Call<ResponseWrapper<BookResponse>>, t: Throwable) {
                onError(t.localizedMessage)
            }

            override fun onResponse(
                call: Call<ResponseWrapper<BookResponse>>,
                response: Response<ResponseWrapper<BookResponse>>
            ) {
                if (response.isSuccessful){
                    val books = response.body()
                    Log.w("2.0 getFeed > ", Gson().toJson(response.body()));
                    books?.let {
//                        booksList.add(books.items)
                        booksResponse.value = books.items

                        loading.value = false
                        error.value = null
                        Log.i("Content of livedata", booksResponse.getValue().toString())
                    }
                }
            }

        })
    }

    private fun onError(message: String) {
        error.value = message
      loading.value = false
    }


}

查询提交/活动

class NavigationActivity : AppCompatActivity(), SearchView.OnQueryTextListener, BooksListFragment.TouchActionDelegate {

    lateinit var searchView: SearchView

    lateinit var viewModel: SearchViewModel

    private val mOnNavigationItemSelectedListener =
        BottomNavigationView.OnNavigationItemSelectedListener { menuItem ->
            when (menuItem.itemId) {R.id.navigation_search -> {
                navigationView.getMenu().setGroupCheckable(0, true, true);
                    replaceFragment(SearchListFragment.newInstance())
                    return@OnNavigationItemSelectedListener true
                }
                R.id.navigation_books -> {
                    navigationView.getMenu().setGroupCheckable(0, true, true);
                    replaceFragment(BooksListFragment.newInstance())
                    return@OnNavigationItemSelectedListener true
                }
            }
            false
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        replaceFragment(SearchListFragment.newInstance())
        navigationView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)

        //Set action bar color
        val actionBar: ActionBar?
        actionBar = supportActionBar
        val colorDrawable = ColorDrawable(Color.parseColor("#FFDAEBE9"))
//        actionBar!!.setBackgroundDrawable(colorDrawable)
//        actionBar.setTitle(("Bobs Books"))
        setSupportActionBar(findViewById(R.id.my_toolbar))
        viewModel = ViewModelProvider(this).get(SearchViewModel::class.java)

    }

    override fun onBackPressed() {
        super.onBackPressed()
        navigationView.getMenu().setGroupCheckable(0, true, true);

    }

    private fun replaceFragment(fragment: Fragment){
        supportFragmentManager
            .beginTransaction()
            .replace(R.id.fragmentHolder, fragment)
            .commit()
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.book_search_menu, menu)
        val searchItem = menu.findItem(R.id.action_search)
        searchView = searchItem.actionView as SearchView
        searchView.setOnQueryTextListener(this)
        searchView.queryHint = "Search for book"
        /*searchView.onActionViewExpanded()
        searchView.clearFocus()*/
//        searchView.setIconifiedByDefault(false)


        return true
    }

    override fun onQueryTextSubmit(query: String): Boolean {
        //replaces fragment if in BooksListFragment when searching
        replaceFragment(SearchListFragment.newInstance())

        val toast = Toast.makeText(
            applicationContext,
            query,
            Toast.LENGTH_SHORT
        )
        toast.show()
        searchView.setQuery("",false)
        searchView.queryHint = "Search for book"
//        viewModel.onAddBook(Book(title = query!!, rating = 5, pages = 329))
        Log.i("Query fra text field", query)
      //  viewModel.getBooks(query)
        return false
    }

    override fun onQueryTextChange(newText: String?): Boolean {
        return false
    }



    override fun launchBookFragment(bookId: Book) {
        supportFragmentManager
            .beginTransaction()
            .replace(R.id.fragmentHolder, com.example.bobsbooks.create.BookFragment.newInstance(bookId.uid))
            .addToBackStack(null)
            .commit()
        navigationView.getMenu().setGroupCheckable(0, false, true);
    }
}

片段

class SearchListFragment : Fragment() {


    lateinit var viewModel: SearchViewModel
    lateinit var contentListView: SearchListView


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_search_list, container, false).apply {
            contentListView = this as SearchListView
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        bindViewModel()
        setContentView()
    }

    private fun setContentView(){
        contentListView.initView()
    }

   private fun bindViewModel(){
       Log.i("ViewmodelCalled", "BindViewModel has been called")
        viewModel = ViewModelProvider(this).get(SearchViewModel::class.java)
        viewModel.booksResponse.observe(viewLifecycleOwner, Observer {list ->
           list?.let {
                Log.i("Observer gets called", "Updatelistgetscalled")
                contentListView.updateList(list)
          }
        } )

        viewModel.error.observe(viewLifecycleOwner, Observer { errorMsg ->
        })

        viewModel.loading.observe(viewLifecycleOwner, Observer { isLoading ->
        })
    }



    companion object {
        fun newInstance(): SearchListFragment {
            return SearchListFragment()
        }
    }

当我将getBooks调用放入Viewmodel Init时,它将正确执行所有操作。它通过API获取书本响应,将其添加到我的LiveData中并通知我的适配器。

但是,如果我改为删除它并通过我的Activity中的Querysubmit调用它,它将根据我的日志获取数据并将其放入我的bookReponse:LiveData中,但这仅是它的全部。从未通知观察者此更改,因此适配器从不知道它有新数据来填充其视图。

我觉得我已经尝试了所有方法,甚至在另一个应用程序中也使用了相同的代码,该代码完全在活动中运行,而不是在活动中进行查询,其余部分在我的片段中调用。我最好的猜测是这会产生影响,但是我不知道如何。

1 个答案:

答案 0 :(得分:0)

根据您的解释

但是,如果我改为删除它并通过我的Activity中的Querysubmit调用它,它将根据我的日志获取数据并将其放入我的bookReponse:LiveData中,但这仅是它的全部。从未通知观察者此更改,因此适配器从不知道它有新数据来填充其视图。

问题是您要在活动和片段中都初始化SearchViewModel,所以片段没有SearchViewModel的相同实例,而是应该在片段中使用共享viewmodel,如:

viewModel = ViewModelProvider(activity).get(SearchViewModel::class.java)