【问题标题】:RecyclerView doesn't display last item when multiple ViewHolders are used使用多个 ViewHolder 时 RecyclerView 不显示最后一项
【发布时间】: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


【解决方案1】:

当我添加两个项目和标题时,标题和一个项目是 显示出来。

问题出在您的 getItemCount 方法中。

override fun getItemCount(): Int {
        return if (items.isEmpty()) 1 else items.filter { it.viewType == ITEM }.size
}

如果你想显示 1 个标题和 2 个元素,这意味着在 recyclerview 中必须有 3 个项目,所以 getItemCount 必须返回 3。但现在看起来 getItemCount 将返回 2,这就是为什么 recycerlview甚至不创建第三个元素。

【讨论】:

    猜你喜欢
    • 2015-06-25
    • 2019-01-26
    • 2021-01-09
    • 2019-03-12
    • 2015-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-25
    相关资源
    最近更新 更多