从使用活动迁移到碎片,得到E/RecyclerView:没有适配器连接;跳过布局,当它以前在MainActivity o



我正在Kotlin中构建一个简单的电影应用程序(练习现场编码采访):使用Retrofit和MVVM架构与Kotlin构建一个电影应用程序。一切都发生在主活动onCreate,我想移动的功能使用片段。我目前得到错误"E/RecyclerView:没有适配器连接;跳过layout"。

我看过很多类似的帖子都没有成功。下面是我的代码。所有日志调用都在发生,除了指示的地方。如果需要更多信息,我会更新问题。

适配器:

package com.example.interviewpracticemvvm.adapter
import android.content.ContentValues.TAG
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.interviewpracticemvvm.databinding.MovieLayoutBinding
import com.example.interviewpracticemvvm.model.Result
class MovieAdapter : RecyclerView.Adapter<MovieAdapter.ViewHolder>() {
private var movieList = ArrayList<Result>()
fun setMovieList(movieList: List<Result>) {
this.movieList = movieList as ArrayList<Result>
notifyDataSetChanged()
Log.d(TAG, "setMovieList called in adapter")
}
class ViewHolder(val binding: MovieLayoutBinding) : RecyclerView.ViewHolder(binding.root) {}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
MovieLayoutBinding.inflate(
LayoutInflater.from(
parent.context
)
)
)
Log.d(TAG, "onCreateViewHolder called") //Not being called
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Glide.with(holder.itemView)
.load("https://image.tmdb.org/t/p/w500" + movieList[position].poster_path)
.into(holder.binding.movieImage)
holder.binding.movieName.text = movieList[position].title
Log.d(TAG, "onBindViewHolder called") // Not being called
}
override fun getItemCount(): Int {
return movieList.size
}
}

片段:

package com.example.interviewpracticemvvm
import android.content.ContentValues.TAG
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.GridLayoutManager
import com.example.interviewpracticemvvm.adapter.MovieAdapter
import com.example.interviewpracticemvvm.databinding.FragmentMovieListBinding
import com.example.interviewpracticemvvm.viewmodel.MovieViewModel
/**
* A simple [Fragment] subclass.
* Use the [MovieListFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class MovieListFragment : Fragment() {
private lateinit var binding: FragmentMovieListBinding
private lateinit var viewModel: MovieViewModel
private lateinit var movieAdapter: MovieAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
Log.d(TAG,"onCreateView: called")
binding = DataBindingUtil.inflate(
inflater, R.layout.fragment_movie_list, container, false
)
return inflater.inflate(R.layout.fragment_movie_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d(TAG,"attaching Adapter")
prepareRecyclerView()
Log.d(TAG,"prepareRecyclerView called")
viewModel = ViewModelProvider(this)[MovieViewModel::class.java]
viewModel.getPopularMovies()
Log.d(TAG,"getPopularMovies called")
viewModel.observeMovieLiveData().observe(viewLifecycleOwner, Observer { movieList ->
movieAdapter.setMovieList(movieList)
Log.d(TAG,"movieAdapter.setMovieList(movieList) called")
})
}
private fun prepareRecyclerView() {
movieAdapter = MovieAdapter()
binding.rvMovies.apply {
layoutManager = GridLayoutManager(activity, 2) //Tried "context"; appCompatActivity used in MainActivity wouldn't work

adapter = movieAdapter
}
Log.d(TAG,"adapter attached")
}
}

Viewmodel:

package com.example.interviewpracticemvvm.viewmodel
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.example.interviewpracticemvvm.RetrofitInstance
import com.example.interviewpracticemvvm.model.Movies
import com.example.interviewpracticemvvm.model.Result
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class MovieViewModel : ViewModel() {
private var movieLiveData = MutableLiveData<List<Result>>()
fun getPopularMovies() {
RetrofitInstance.api.getPopularMovies("69d66957eebff9666ea46bd464773cf0")
.enqueue(object : Callback<Movies> {
override fun onResponse(call: Call<Movies>, response: Response<Movies>) {
if (response.body() != null) {
movieLiveData.value = response.body()!!.results
}
}
override fun onFailure(call: Call<Movies>, t: Throwable) {
Log.d("TAG", t.message.toString())
}
})
}
fun observeMovieLiveData() : LiveData<List<Result>> {
return movieLiveData
}
}

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"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MovieListFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_movies"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="@layout/movie_layout"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

我在使用数据绑定和安全时出现了一系列错误。我还没有找到一篇文章指出一个明显的问题。这个错误似乎不够具体,它可能是由项目中的许多事情引起的。

您正在膨胀布局的两个副本-一个是由您的binding对象使用的,另一个是实际显示的内容:

// calling an 'inflate' function twice
binding = DataBindingUtil.inflate(
inflater, R.layout.fragment_movie_list, container, false
)
// this is the actual inflated View that gets displayed (because it's the one you return)
return inflater.inflate(R.layout.fragment_movie_list, container, false)

所以有两个不同的RecyclerView-一个在布局中所有的binding引用指向,和一个实际显示在屏幕上。当你调用prepareRecyclerView时,被设置的不是屏幕上的那个:

binding.rvMovies.apply {
layoutManager = GridLayoutManager(activity, 2)
adapter = movieAdapter
}

这意味着当显示时一个得到它的布局通行证(即它被告知它正在被绘制),它没有一个适配器或布局管理器,因为你没有设置他们在RecyclerView。这就是为什么你会得到错误。


你需要充气一个您的布局的副本,传递回显示,并绑定到相同的实例,这样您就可以访问它的视图。有两种方法:

// use the Data Binding stuff to inflate the view and get your binding object
binding = DataBindingUtil.inflate(
inflater, R.layout.fragment_movie_list, container, false
)
// pass the inflated layout (its root view) back for display
return binding.root

// inflate the view hierarchy yourself
val view = inflater.inflate(R.layout.fragment_movie_list, container, false)
// bind to it (like the inflate call, but without inflating anything -
// just does all the lookups using the view hierarchy you've passed in)
binding = DataBindingUtil.bind(view)
// return the view for display
return view

第一种方法(通过绑定类膨胀,传回root)更简洁,是推荐的做事方式,也适用于Activities。当你已经有了一个现有的视图层次结构,并且你只想把所有的视图连接到绑定对象中的引用时,bind是有用的。


基本上,如果你做过多次布局膨胀这是一个很大的警告信号——通常你不会这样做,并且拥有两个不同的、完全独立的视图集可能会导致bug。通常这种情况下,配置的和显示的是不同的,所以屏幕上的东西不工作

只需通过requireContext()而不是activity,并尝试如下,我希望你的问题解决。

private fun prepareRecyclerView() {
movieAdapter = MovieAdapter()
binding.rvMovies.apply {
layoutManager = GridLayoutManager(requireContext(), 2) //Tried "context"; appCompatActivity used in MainActivity wouldn't work
adapter = movieAdapter
}
Log.d(TAG,"adapter attached")
}

相关内容

  • 没有找到相关文章

最新更新