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

Observer isn't notified when data has been changed

我有一个 activity,它有一个 SearchView,我用它来输入查询,然后我的应用程序使用查询来访问 API。我的 activity 还包含一个片段,在这个片段中我有我的观察者。

此外,我还有我的 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
    }


}

查询提交/Activity

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 获取 bookresponse,将其添加到我的 LiveData 并通知我的适配器。

但是,如果我改为删除它并通过我的 Activity 中的 Querysubmit 调用它,它将根据我的日志获取数据并将其放入我的 booksReponse:LiveData,但仅此而已它所做的一切。观察者永远不会收到此更改的通知,因此适配器永远不知道它有新数据来填充其视图。

我觉得我已经尝试了所有方法,我什至在另一个应用程序中使用了基本相同的代码,它完全在 activity 中运行,而不是在 activity 中进行查询,休息在我的片段中被称为。我最好的猜测是这会产生影响,但我不知道如何影响。

根据您的解释

However, if I instead delete that and call it through my Querysubmit in my Activity, it will, according to my logs, get the data and put it into my booksReponse:LiveData, but thats all it does. The observer is never notifed of this change, and thus the adapter never knows that it has new data to populate its views.

问题是您正在 activity 和片段中初始化 SearchViewModel,因此片段没有相同的 SearchViewModel 实例,您应该在片段中使用共享视图模型,例如:

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