RecyclerView中具有多种视图类型的Recyclerview显示错误的布局

时间:2018-10-31 05:13:08

标签: java android android-recyclerview kotlin

我有一个父级布局,其中有一些TextViews和RecyclerView。还有 child 布局,其中有一个RecyclerView,适配器有5种不同的视图类型

对于 recyclerview,我使用的适配器具有5种不同的布局,其中包括

  1. VIEW_TYPE_LANDSCAPE
  2. VIEW_TYPE_LANDSCAPE_CARD
  3. VIEW_TYPE_PORTRAIT
  4. VIEW_TYPE_SQUARE
  5. VIEW_TYPE_TWO_COLUMN

RecyclerView中包含超过 x 个项目时,就会发生此问题。当我向下滚动到屏幕底部,然后向上滚动到屏幕顶部并再次向下滚动到屏幕底部时, RecyclerView 会显示错误的布局/查看类型。

parent_layout.xml

<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="home.HomeTabViewModel" />
</data>

<android.support.constraint.ConstraintLayout
    android:id="@+id/cl_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include
        android:id="@+id/v_toolbar"
        layout="@layout/toolbar_home_v3" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_home_content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:clipToPadding="false"
        android:overScrollMode="never"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/v_toolbar" />

</android.support.constraint.ConstraintLayout>
</layout>

child_layout.xml

<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">

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/dimens_32dp">

    <TextView
        android:id="@+id/tv_title"
        style="@style/TextSoftBlackBold.20sp"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/dimens_16dp"
        android:layout_marginRight="@dimen/dimens_16dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        tools:text="Popular Ideas" />

    <TextView
        android:id="@+id/tv_subtitle"
        style="@style/TextDarkGrayNormal.14sp"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/dimens_16dp"
        android:layout_marginTop="@dimen/dimens_7dp"
        android:layout_marginRight="@dimen/dimens_16dp"
        android:ellipsize="end"
        android:maxLines="2"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_title"
        tools:text="Popular events happening during your stay in Bali" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_menu_grid"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dimens_2dp"
        app:layout_constraintTop_toBottomOf="@+id/tv_subtitle" />


</android.support.constraint.ConstraintLayout>
</layout>

BaseMenuAdapter.kt

class BaseMenuAdapter(val context: Context?,
                           private var contents: List<HomeViewParam.Content>?,
                  private val menuGrid: HomeItem.MenuGrid?) :
    RecyclerView.Adapter<BaseMenuAdapter.MyViewHolder>() {

companion object {
    const val VIEW_TYPE_LANDSCAPE = 0
    const val VIEW_TYPE_LANDSCAPE_CARD = 1
    const val VIEW_TYPE_PORTRAIT = 2
    const val VIEW_TYPE_TWO_COLUMN = 3
    const val VIEW_TYPE_SQUARE = 4
}

inner class MyViewHolder : RecyclerView.ViewHolder {
    var landscapeBinding: ViewTemplateLandscapeBinding? = null
    var landscapeCardBinding: ViewTemplateLandscapeCardBinding? = null
    var portraitBinding: ItemHomeMenuPortraitBinding? = null
    var twoColumnBinding: ItemHomeMenuTwoColumnBinding? = null
    var squareBinding: ItemHomeMenuSquareBinding? = null


    constructor(binding: ViewTemplateLandscapeBinding) : super(binding.root) {
        landscapeBinding = binding
    }

    constructor(binding: ViewTemplateLandscapeCardBinding) : super(binding.root) {
        landscapeCardBinding = binding
    }

    constructor(binding: ItemHomeMenuPortraitBinding) : super(binding.root) {
        portraitBinding = binding
    }

    constructor(binding: ItemHomeMenuTwoColumnBinding) : super(binding.root) {
        twoColumnBinding = binding
    }

    constructor(binding: ItemHomeMenuSquareBinding) : super(binding.root) {
        squareBinding = binding
    }
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseMenuAdapter.MyViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    val binding: ViewDataBinding
    return when (viewType) {
        0 -> {
            binding = DataBindingUtil.inflate(inflater, R.layout.view_template_landscape, parent, false)
            MyViewHolder(binding as ViewTemplateLandscapeBinding)
        }
        1 -> {
            binding = DataBindingUtil.inflate(inflater, R.layout.view_template_landscape_card, parent, false)
            MyViewHolder(binding as ViewTemplateLandscapeCardBinding)
        }
        2 -> {
            binding = DataBindingUtil.inflate(inflater, R.layout.item_home_menu_portrait, parent, false)
            MyViewHolder(binding as ItemHomeMenuPortraitBinding)
        }
        3 -> {
            binding = DataBindingUtil.inflate(inflater, R.layout.item_home_menu_two_column, parent, false)
            MyViewHolder(binding as ItemHomeMenuTwoColumnBinding)
        }
        4 -> {
            binding = DataBindingUtil.inflate(inflater, R.layout.item_home_menu_square, parent, false)
            MyViewHolder(binding as ItemHomeMenuSquareBinding)
        }
        else -> {
            throw RuntimeException("The type has to be ONE or TWO")
        }
    }
}

override fun onBindViewHolder(holder: BaseMenuAdapter.MyViewHolder, position: Int) {
    context?.resources.run {
        when (holder.itemViewType) {
            VIEW_TYPE_LANDSCAPE -> {
                val binding = holder.landscapeBinding
                binding?.run {
                    val item = contents?.let { it[holder.adapterPosition] }
                    binding.content = item
                    root.setOnClickListener { AllWebViewActivityV2.startThisActivity(context, item?.title ?: "",
                            item?.url ?: "") }
                    executePendingBindings()
                }
            }
            VIEW_TYPE_LANDSCAPE_CARD -> {
                val binding = holder.landscapeCardBinding
                binding?.run {
                    val item = contents?.let { it[holder.adapterPosition] }
                    binding.content = item
                    root.setOnClickListener { AllWebViewActivityV2.startThisActivity(context, item?.title ?: "",
                            item?.url ?: "") }
                    executePendingBindings()
                }
            }
            VIEW_TYPE_TWO_COLUMN -> {
                val binding = holder.twoColumnBinding
                binding?.run {
                    val item = contents?.let { it[holder.adapterPosition] }
                    binding.content = item
                    root.setOnClickListener { AllWebViewActivityV2.startThisActivity(context, item?.title ?: "",
                            item?.url ?: "") }
                    executePendingBindings()
                }
            }
            VIEW_TYPE_PORTRAIT -> {
                val binding = holder.portraitBinding
                binding?.run {
                    val item = contents?.let { it[holder.adapterPosition] }
                    binding.content = item
                    root.setOnClickListener { AllWebViewActivityV2.startThisActivity(context, item?.title ?: "",
                            item?.url ?: "") }
                    executePendingBindings()
                }
            }
            VIEW_TYPE_SQUARE -> {
                val binding = holder.squareBinding
                binding?.run {
                    val item = contents?.let { it[holder.adapterPosition] }
                    binding.content = item
                    root.setOnClickListener { AllWebViewActivityV2.startThisActivity(context, item?.title ?: "",
                            item?.url ?: "") }
                    executePendingBindings()
                }
            }
            else -> {}
        }
    }
}

override fun getItemViewType(position: Int): Int {
    contents?.let {
        return when (menuGrid?.templateType) {
            TemplateType.LANDSCAPE -> VIEW_TYPE_LANDSCAPE
            TemplateType.LANDSCAPE_CARD -> VIEW_TYPE_LANDSCAPE_CARD
            TemplateType.TWO_COLUMN -> VIEW_TYPE_TWO_COLUMN
            TemplateType.POTRAIT -> VIEW_TYPE_PORTRAIT
            TemplateType.SQUARE -> VIEW_TYPE_SQUARE
            else -> -1
        }
    }
    return -1
}

override fun getItemCount(): Int {
    return contents?.size ?: 0
}

override fun setHasStableIds(hasStableIds: Boolean) {
    super.setHasStableIds(false)
}

override fun getItemId(position: Int): Long {
    return position.toLong()
}

fun setItems(contents: List<HomeViewParam.Content>?) {
    this.contents = contents
}
}

编辑 这是我的观点 enter image description here

因此,如上所述,当我向上或向下滚动时,某些项目有时会使用错误的视图类型

1 个答案:

答案 0 :(得分:2)

您需要为每种布局类型定义一个ViewHolder,然后分别使用 getItemViewType() onBindViewHolder(),大致如下:

@Override
public int getItemViewType(int position) {
    if (isValidPosition(position)) {
        Data d = mDataset[position];

        if (d instanceof LandscapeData) {
            return VIEW_TYPE_LANDSCAPE;
        } else if (d instanceof PortraitData) {
            return VIEW_TYPE_PORTRAIT;
        }
        // more else-ifs here
    }
    // default to landscape
    return VIEW_TYPE_LANDSCAPE;
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    Data d = mDataset[position];

    if (holder instanceof LandscapeVH) {
        // binding of dataset for landscape layouts
        LandscapeData data = (LandscapeData) d;
        LandscapeVH vh = (LandscapeVH) holder;
        // bind the data to the view on the fly here
        vh.myTextView.setText(data.getLandscapeTitle());
    } else if (holder instanceof PortraitVH) {
        // binding of dataset for portrait layouts
        PortraitData data = (PortraitData) d;
        PortraitVH vh = (PortraitVH) holder;
        // bind the data to the view on the fly here
        vh.myTextView.setText(data.getPortraitTitle());
    }
    // more else-ifs here
}

注意:您的mDataset []是唯一的事实来源,它包含数据类型的数据集,这些数据集可以子类化为更专业的数据集,例如LandscapeData和PortraitData,它们与布局的类型紧密相关。