Recyclerview с несколькими типами просмотра внутри RecyclerView показывает неправильный макет

У меня есть родительский макет, в нем есть TextViews и RecyclerView. И макет ребенка , он имеет 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
}
}

EDIT Вот мое мнение выглядит так: введите описание изображения здесь

Как я уже упоминал выше, когда я просматриваю вверх или вниз, иногда некоторые элементы используют неправильный вид

Всего 1 ответ


Вам нужно определить один ViewHolder для каждого из ваших типов макета, а затем вам нужно использовать каждый тип экземпляра класса 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 [] является вашим единственным источником правды, и он содержит наборы данных типа Data, которые могут быть подклассифицированы в более специализированные наборы данных, такие как LandscapeData и PortraitData, которые идут рука об руку с типами вашего макета.


Есть идеи?

10000