为 RecyclerView 片段实现 ListAdapter
Implementing ListAdapter for a RecyclerView Fragment
我已经用普通的 ViewAdapter 成功地完成了这个,但是我似乎无法让它与 ListAdapter 一起工作。
这是我的 片段,它完成了大部分工作:
class CrimeListFragment: Fragment() {
//Required interface for hosting activities
interface Callbacks {
fun onCrimeSelected(crimeId: UUID)
}
private var callbacks: Callbacks? = null
private lateinit var crimeRecyclerView: RecyclerView
private val crimeListViewModel: CrimeListViewModel by lazy {
ViewModelProviders.of(this).get(CrimeListViewModel::class.java)
}
override fun onAttach(context: Context) {
super.onAttach(context)
callbacks = context as Callbacks?
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_crime_list, container, false)
crimeRecyclerView =
view.findViewById(R.id.crime_recycler_view) as RecyclerView
crimeRecyclerView.layoutManager = LinearLayoutManager(context)
crimeRecyclerView.adapter = CrimeListAdapter(emptyList())
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
crimeListViewModel.crimeListLiveData.observe(
viewLifecycleOwner,
Observer { crimes ->
crimes?.let {
Log.i(TAG, "Got crimes ${crimes.size}")
updateUI(crimes)
}
}
)
}
override fun onDetach() {
super.onDetach()
callbacks = null
}
private fun updateUI(crimes: List<Crime>) {
crimeRecyclerView.adapter = CrimeListAdapter(crimes)
}
companion object {
fun newInstance(): CrimeListFragment {
return CrimeListFragment()
}
}
private inner class CrimeHolder(view: View)
: RecyclerView.ViewHolder(view), View.OnClickListener {
private lateinit var crime: Crime
private val titleTextView = itemView.findViewById<TextView>(R.id.crime_title)
private val dateTextView = itemView.findViewById<TextView>(R.id.crime_date)
private val solvedImageView = itemView.findViewById<ImageView>(R.id.crime_solved)
init {
itemView.setOnClickListener(this)
}
fun bind(crime: Crime) {
this.crime = crime
titleTextView.text = crime.title
dateTextView.text = crime.date.toString()
solvedImageView.visibility = if(crime.isSolved) {
View.VISIBLE
} else {
View.GONE
}
}
override fun onClick(v: View) {
callbacks?.onCrimeSelected(crime.id)
}
}
private inner class CrimeListAdapter(var crimes: List<Crime>)
: ListAdapter<Crime, CrimeHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CrimeHolder {
val view =
layoutInflater.inflate(R.layout.list_item_crime, parent, false)
return CrimeHolder(view)
}
override fun onBindViewHolder(holder: CrimeHolder, position: Int) {
holder.bind(crimes[position])
}
}
private inner class DiffCallback: DiffUtil.ItemCallback<Crime>() {
override fun areItemsTheSame(oldItem: Crime, newItem: Crime): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Crime, newItem: Crime): Boolean {
return oldItem == newItem
}
}
}
这是片段的视图模型:
class CrimeListViewModel: ViewModel() {
private val crimeRepository = CrimeRepository.get()
val crimeListLiveData = crimeRepository.getCrimes() //returns LiveData<List<Crime>>
}
Android 文档中有关于 ListAdapter 的内容:
While using a LiveData is an easy way to provide data to the adapter, it isn't required - you can use submitList(List) when new lists are available.
我应该在每次更新 UI 时提交一个新列表,而不是创建一个新的 ListAdapter 对象。但是 crimeRecyclerView.adapter
没有 .submitList()
功能。那么如何传递新列表呢?
LiveData 对我来说还是新手所以我不是很清楚。我已经观察到 LiveData 存储在我的视图模型中。那么这次我观察了什么?或者我只是将代码添加到我现有的 Observer?
最后,当我 运行 代码处于这种状态时,phone 显示一个空的 RecyclerView。仅 UpdateUI()
被调用,CrimeListAdapter
的函数中的 none 被调用。我不确定这是一个真正的问题还是上述问题的结果。
1.I'm supposed to submit a new list instead of creating a new ListAdapter object each time I update the UI. But
crimeRecyclerView.adapter has no .submitList() function. So how do I
pass on the new list?
crimeRecyclerView.adapter
return RecyclerView.Adapter 类型
submitList()
是ListAdapter的一个方法,是RecyclerView.Adapter
的子class
在调用该方法之前,您需要从 super 转换为 sub class,就像这样。
(crimeRecyclerView.adapter as CrimeListAdapter).submitList(crimes)
2.LiveData is still new to me so I'm not quite clear on this. I already observe a LiveData stored in my viewmodel. So what do I
observe this time? Or do I just add code to my existing Observer?
这部分你的代码很好,不需要做更多。
3.Finally when I run the code in this state, phone shows an empty RecyclerView. Only UpdateUI() gets called, none of CrimeListAdapter's
functions get called. I'm not sure if this is a real problem or just
the consequence of the above.
使用 ListAdapter 的最好的部分是您不需要向构造函数提供数据列表(在您的案例中是犯罪)。
回到你的代码,你需要改变 3 件事。
// crimeRecyclerView.adapter = CrimeListAdapter(emptyList())
crimeRecyclerView.adapter = CrimeListAdapter()
和
// crimeRecyclerView.adapter = CrimeListAdapter(crimes)
(crimeRecyclerView.adapter as CrimeListAdapter).submitList(crimes)
和
//private inner class CrimeListAdapter(var crimes: List<Crime>) :
// ListAdapter<Crime, CrimeHolder>(DiffCallback()) {
//
// override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CrimeHolder {
// val view = layoutInflater.inflate(R.layout.list_item_crime, parent, false)
// return CrimeHolder(view)
// }
//
// override fun onBindViewHolder(holder: CrimeHolder, position: Int) {
// holder.bind(crimes[position])
// }
//}
private inner class CrimeListAdapter : ListAdapter<Crime, CrimeHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CrimeHolder {
val view = layoutInflater.inflate(R.layout.list_item_crime, parent, false)
return CrimeHolder(view)
}
override fun onBindViewHolder(holder: CrimeHolder, position: Int) {
holder.bind(getItem(position))
}
}
我已经用普通的 ViewAdapter 成功地完成了这个,但是我似乎无法让它与 ListAdapter 一起工作。
这是我的 片段,它完成了大部分工作:
class CrimeListFragment: Fragment() {
//Required interface for hosting activities
interface Callbacks {
fun onCrimeSelected(crimeId: UUID)
}
private var callbacks: Callbacks? = null
private lateinit var crimeRecyclerView: RecyclerView
private val crimeListViewModel: CrimeListViewModel by lazy {
ViewModelProviders.of(this).get(CrimeListViewModel::class.java)
}
override fun onAttach(context: Context) {
super.onAttach(context)
callbacks = context as Callbacks?
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_crime_list, container, false)
crimeRecyclerView =
view.findViewById(R.id.crime_recycler_view) as RecyclerView
crimeRecyclerView.layoutManager = LinearLayoutManager(context)
crimeRecyclerView.adapter = CrimeListAdapter(emptyList())
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
crimeListViewModel.crimeListLiveData.observe(
viewLifecycleOwner,
Observer { crimes ->
crimes?.let {
Log.i(TAG, "Got crimes ${crimes.size}")
updateUI(crimes)
}
}
)
}
override fun onDetach() {
super.onDetach()
callbacks = null
}
private fun updateUI(crimes: List<Crime>) {
crimeRecyclerView.adapter = CrimeListAdapter(crimes)
}
companion object {
fun newInstance(): CrimeListFragment {
return CrimeListFragment()
}
}
private inner class CrimeHolder(view: View)
: RecyclerView.ViewHolder(view), View.OnClickListener {
private lateinit var crime: Crime
private val titleTextView = itemView.findViewById<TextView>(R.id.crime_title)
private val dateTextView = itemView.findViewById<TextView>(R.id.crime_date)
private val solvedImageView = itemView.findViewById<ImageView>(R.id.crime_solved)
init {
itemView.setOnClickListener(this)
}
fun bind(crime: Crime) {
this.crime = crime
titleTextView.text = crime.title
dateTextView.text = crime.date.toString()
solvedImageView.visibility = if(crime.isSolved) {
View.VISIBLE
} else {
View.GONE
}
}
override fun onClick(v: View) {
callbacks?.onCrimeSelected(crime.id)
}
}
private inner class CrimeListAdapter(var crimes: List<Crime>)
: ListAdapter<Crime, CrimeHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CrimeHolder {
val view =
layoutInflater.inflate(R.layout.list_item_crime, parent, false)
return CrimeHolder(view)
}
override fun onBindViewHolder(holder: CrimeHolder, position: Int) {
holder.bind(crimes[position])
}
}
private inner class DiffCallback: DiffUtil.ItemCallback<Crime>() {
override fun areItemsTheSame(oldItem: Crime, newItem: Crime): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Crime, newItem: Crime): Boolean {
return oldItem == newItem
}
}
}
这是片段的视图模型:
class CrimeListViewModel: ViewModel() {
private val crimeRepository = CrimeRepository.get()
val crimeListLiveData = crimeRepository.getCrimes() //returns LiveData<List<Crime>>
}
Android 文档中有关于 ListAdapter 的内容:
While using a LiveData is an easy way to provide data to the adapter, it isn't required - you can use submitList(List) when new lists are available.
我应该在每次更新 UI 时提交一个新列表,而不是创建一个新的 ListAdapter 对象。但是
crimeRecyclerView.adapter
没有.submitList()
功能。那么如何传递新列表呢?LiveData 对我来说还是新手所以我不是很清楚。我已经观察到 LiveData 存储在我的视图模型中。那么这次我观察了什么?或者我只是将代码添加到我现有的 Observer?
最后,当我 运行 代码处于这种状态时,phone 显示一个空的 RecyclerView。仅
UpdateUI()
被调用,CrimeListAdapter
的函数中的 none 被调用。我不确定这是一个真正的问题还是上述问题的结果。
1.I'm supposed to submit a new list instead of creating a new ListAdapter object each time I update the UI. But crimeRecyclerView.adapter has no .submitList() function. So how do I pass on the new list?
crimeRecyclerView.adapter
return RecyclerView.Adapter 类型
submitList()
是ListAdapter的一个方法,是RecyclerView.Adapter
在调用该方法之前,您需要从 super 转换为 sub class,就像这样。
(crimeRecyclerView.adapter as CrimeListAdapter).submitList(crimes)
2.LiveData is still new to me so I'm not quite clear on this. I already observe a LiveData stored in my viewmodel. So what do I observe this time? Or do I just add code to my existing Observer?
这部分你的代码很好,不需要做更多。
3.Finally when I run the code in this state, phone shows an empty RecyclerView. Only UpdateUI() gets called, none of CrimeListAdapter's functions get called. I'm not sure if this is a real problem or just the consequence of the above.
使用 ListAdapter 的最好的部分是您不需要向构造函数提供数据列表(在您的案例中是犯罪)。
回到你的代码,你需要改变 3 件事。
// crimeRecyclerView.adapter = CrimeListAdapter(emptyList())
crimeRecyclerView.adapter = CrimeListAdapter()
和
// crimeRecyclerView.adapter = CrimeListAdapter(crimes)
(crimeRecyclerView.adapter as CrimeListAdapter).submitList(crimes)
和
//private inner class CrimeListAdapter(var crimes: List<Crime>) :
// ListAdapter<Crime, CrimeHolder>(DiffCallback()) {
//
// override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CrimeHolder {
// val view = layoutInflater.inflate(R.layout.list_item_crime, parent, false)
// return CrimeHolder(view)
// }
//
// override fun onBindViewHolder(holder: CrimeHolder, position: Int) {
// holder.bind(crimes[position])
// }
//}
private inner class CrimeListAdapter : ListAdapter<Crime, CrimeHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CrimeHolder {
val view = layoutInflater.inflate(R.layout.list_item_crime, parent, false)
return CrimeHolder(view)
}
override fun onBindViewHolder(holder: CrimeHolder, position: Int) {
holder.bind(getItem(position))
}
}