【问题标题】:Observer isn't notified when data has been changed数据更改时不会通知观察者
【发布时间】:2020-12-26 00:12:36
【问题描述】:

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

此外,我还有我的 ViewModel,它会在给定查询时调用 API。但是,我的观察者永远不会收到有关更新的通知,因此我的视图永远不会更新。除非我在启动时直接从我的 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 获取 bookresponse,将其添加到我的 LiveData 并通知我的适配器。

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

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

【问题讨论】:

  • 也发布您的活动代码
  • 刚刚添加了,谢谢关注!
  • 可能list 为空?

标签: android android-livedata observers


【解决方案1】:

根据你的解释

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

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

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

【讨论】:

  • 确切的变化最终是: viewModel = ViewModelProvider(requireActivity()).get(SearchViewModel::class.java) 但它有效,你是对的。非常感谢,非常感谢!
猜你喜欢
  • 1970-01-01
  • 2014-03-23
  • 2019-09-18
  • 2023-03-29
  • 1970-01-01
  • 2017-03-13
  • 1970-01-01
  • 2020-02-01
  • 1970-01-01
相关资源
最近更新 更多