【发布时间】:2020-08-27 19:10:00
【问题描述】:
我正在使用androidx.recyclerview.widget.RecyclerView 显示项目列表,由其他项目分隔为带有一些聚合值的“标题”。
当我在我的列表中只放一个项目而不添加标题时,一切正常并且项目显示正确。一旦我添加了标题项,就只显示标题并且不显示单个项目。 当我添加两个项目和标题时,会显示标题和一个项目。我不知道为什么我的列表中的最后一项虽然存在于适配器数据源中,但它却丢失了。
我的 ListAdapter 继承自 RecyclerView.Adapter<RecyclerView.ViewHolder> 并使用我的列表项的 viewType 属性检测到的两个 ViewHolder。
加载数据时,不会为列表中的最后一项调用onBindViewHolder 方法,即使该项目在我屏幕的可见部分中也是如此。
有人有提示吗,为什么会这样?
class ListAdapter(val onClick: (position: Long) -> Unit,
val onLongClick: (Long) -> Unit,
val onShareClick: (id: Long?) -> Unit) : RecyclerView.Adapter<RecyclerView.ViewHolder>(),
BindableAdapter<List<ListAdapterItem<*>>> {
var items: List<ListAdapterItem<*>> = emptyList()
private var actionMode: ActionMode? = null
var tracker: SelectionTracker<Long>? = null
init {
setHasStableIds(true)
}
override fun setData(data: List<ListAdapterItem<*>>) {
this.items = data // all items are set correctly here!!
notifyDataSetChanged()
}
override fun getItemViewType(position: Int): Int {
return if (items.isEmpty()) EMPTY else items[position].viewType
}
override fun getItemCount(): Int {
return if (items.isEmpty()) 1 else items.filter { it.viewType == ITEM }.size
}
override fun getItemId(position: Int): Long = position.toLong()
fun getItem(position: Long): ListViewModel.ListItem = item[position.toInt()].value as ListViewModel.ListItem
fun setActionMode(actionMode: ActionMode?) {
this.actionMode = actionMode
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
EMPTY -> EmptyViewHolder(parent)
HEADER -> HistoryGroupHeaderViewHolder(parent)
else -> HistoryViewHolder(parent)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is HistoryViewHolder) {
val item = items[position].value as ListViewModel.ListItem
tracker?.let {
holder.bind(item, it.isSelected(position.toLong()))
}
holder.itemView.setOnClickListener {
onClick(position.toLong())
}
holder.itemView.findViewById<AppCompatImageView>(R.id.history_item_share)?.setOnClickListener {
onShareClick(item.id)
}
}
else if (holder is HistoryGroupHeaderViewHolder) {
val header = items[position].value as ListViewModel.ListSectionHeader
holder.bind(header)
}
}
class HistoryViewHolder(
private val parent: ViewGroup,
private val binding: at.app.databinding.ViewHistoryListItemBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.view_history_list_item,
parent,
false
)
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ListViewModel.ListItem, isActivated: Boolean = false) {
binding.model = item
itemView.isActivated = isActivated
val imageView = itemView.findViewById<AppCompatImageView>(R.id.history_item_image)
if(itemView.isActivated) {
val parameter = imageView?.layoutParams as ConstraintLayout.LayoutParams
parameter.setMargins(
parent.context.resources.getDimension(R.dimen.spacing_small).toInt(),
parent.context.resources.getDimension(R.dimen.spacing_small).toInt(),
parent.context.resources.getDimension(R.dimen.spacing_small).toInt(),
parent.context.resources.getDimension(R.dimen.spacing_small).toInt()
)
imageView.layoutParams = parameter
} else {
val parameter = imageView?.layoutParams as ConstraintLayout.LayoutParams
parameter.setMargins(0,0,0,0)
imageView.layoutParams = parameter
}
}
fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
object : ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition(): Int = adapterPosition
override fun getSelectionKey(): Long? = itemId
}
}
class HistoryGroupHeaderViewHolder(
private val parent: ViewGroup,
private val binding: at.app.databinding.ViewHistoryListGroupHeaderItemBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.view_history_list_group_header_item,
parent,
false
)
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ListViewModel.ListSectionHeader) {
binding.model = item
}
}
class EmptyViewHolder(
private val parent: ViewGroup, view: View = LayoutInflater.from(parent.context).inflate(
R.layout.view_history_empty_item,
parent,
false
)
) : RecyclerView.ViewHolder(view)
companion object {
const val EMPTY = 0
const val ITEM = 1
const val HEADER = 2
}
}
class MyItemDetailsLookup(private val recyclerView: RecyclerView) : ItemDetailsLookup<Long>() {
private val log = LoggerFactory.getLogger(ListAdapter::class.java)
override fun getItemDetails(e: MotionEvent): ItemDetails<Long>? {
val view = recyclerView.findChildViewUnder(e.x, e.y)
if (view != null) {
return try {
if(recyclerView.getChildViewHolder(view) is ListAdapter.HistoryViewHolder) {
(recyclerView.getChildViewHolder(view) as ListAdapter.HistoryViewHolder)
.getItemDetails()
} else {
null
}
} catch (ex: Exception) {
log.error("Error on getItemDetails. ", ex)
null
}
}
return null
}
}
data class ListAdapterItem<out T>(val value: T, val viewType: Int)
这是我的布局:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
<variable
name="viewModel"
type="at.app.ui.viewmodel.ListViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/list_app_bar"
layout="@layout/layout_toolbar" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/history_recycler_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/transparent"
android:scrollbars="vertical"
app:data="@{viewModel.items}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/list_app_bar" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
【问题讨论】:
-
你必须给一个示例代码
-
对不起,我现在添加了我的 ListAdpater 和 xml 布局。
标签: android android-recyclerview