onBindViewHolder绑定视图不一致

时间:2019-05-14 16:12:34

标签: android android-recyclerview

我有一个ViewHolder,它的显示方式会有所不同,具体取决于它是位于带有RecyclerView的两列GridLayoutManager的左侧还是右侧。请注意视图两侧的连接线:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="wrap_content"
              android:layout_marginTop="12px"
              android:layout_marginBottom="12px"
              android:layout_gravity="center"
              android:gravity="center_vertical"
              android:layout_height="wrap_content"
              android:id="@+id/citation_select_holder">
    <ImageView
            android:src="@drawable/connector_line"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/citation_select_connector_right"/>


    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    xmlns:tools="http://schemas.android.com/tools"
                    android:layout_width="348px"
                    android:layout_height="104px"
                    android:layout_weight="1"
                    android:background="@drawable/button_background_white"
                    android:id="@+id/citation_select_citation_holder">

        <LinearLayout android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:orientation="vertical"
                      android:layout_centerVertical="true"
                      android:layout_alignParentLeft="true"
                      android:layout_marginLeft="28px"

        >
            <TextView
                    tools:text="123456"
                    android:textAppearance="@style/citation_select_item_number"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/citation_select_citation_number_text"/>

            <TextView
                    tools:text="Pay by: Nov 18th, 2019"
                    android:textAppearance="@style/citation_select_item_due_date"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/citation_select_due_date_text"/>
            <TextView
                    tools:text="a category label"
                    android:textAppearance="@style/citation_select_item_category"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/citation_select_category_text"/>

        </LinearLayout>

        <TextView
                tools:text="$10.00"
                android:textAppearance="@style/citation_select_item_cost"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:id="@+id/citation_select_cost_text" android:layout_marginRight="28px"/>

    </RelativeLayout>

    <ImageView
            android:src="@drawable/connector_line"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/citation_select_connector_left"/>


</LinearLayout>

ViewHolder

调用onBindViewHolder时,远离视图所在一侧的连接器线将消失,并且边距也会相应更新。

if (position % 2 == 0) {

    holder.itemView.findViewById<ImageView>(R.id.citation_select_connector_left).visibility = View.GONE
    val marginLayoutParams1 = holder.citationHolder.layoutParams as GridLayoutManager.LayoutParams
    marginLayoutParams1.setMargins(0, 12, 12, 12)
    holder.itemView.findViewById<LinearLayout>(R.id.citation_select_holder).layoutParams =
        marginLayoutParams1

} else {

    holder.itemView.findViewById<ImageView>(R.id.citation_select_connector_right).visibility = View.GONE
    val marginLayoutParams2 = holder.citationHolder.layoutParams as GridLayoutManager.LayoutParams
    marginLayoutParams2.setMargins(12, 12, 0, 12)
    holder.itemView.findViewById<LinearLayout>(R.id.citation_select_holder).layoutParams =
        marginLayoutParams2
}

仅通过屏幕上的按钮以六个增量进行滚动。前两个页面正常加载:

Desired Behavior

但是模式在引用14时就开始崩溃。请记住,引文编号对应的是视图在RecyclerView中的位置:

Actual behavior

正在发生什么改变行为?

2 个答案:

答案 0 :(得分:6)

我想我知道有什么可以帮助您解决此问题的。我假设它正在重用旧视图,因为RecyclerView应该并且在代码中的任何地方都没有一行将connector行的可见性设置回可见。

您应该同时添加GONE可见性和将其他可见性设置为可见的代码:

if (position % 2 == 0) {

holder.itemView.findViewById<ImageView>(R.id.citation_select_connector_right).visibility = View.VISIBLE
holder.itemView.findViewById<ImageView>(R.id.citation_select_connector_left).visibility = View.GONE
val marginLayoutParams1 = holder.citationHolder.layoutParams as GridLayoutManager.LayoutParams
marginLayoutParams1.setMargins(0, 12, 12, 12)
holder.itemView.findViewById<LinearLayout>(R.id.citation_select_holder).layoutParams =
    marginLayoutParams1

} else {

holder.itemView.findViewById<ImageView>(R.id.citation_select_connector_left).visibility = View.VISIBLE
holder.itemView.findViewById<ImageView>(R.id.citation_select_connector_right).visibility = View.GONE
val marginLayoutParams2 = holder.citationHolder.layoutParams as GridLayoutManager.LayoutParams
marginLayoutParams2.setMargins(12, 12, 0, 12)
holder.itemView.findViewById<LinearLayout>(R.id.citation_select_holder).layoutParams =
    marginLayoutParams2
}

其他说明

RecyclerView将重用一些旧视图,对吗?好吧,由于这两行都以VISIBLE开头,因此您假设这是它们的默认状态。但是,当您将线设置为GONE时,您永远不会将它们放回可见状态,因此,如果RecyclerView重用该视图,则不会在此处添加边距,而只会丢失连接线。您总是希望onBindViewHolder中的每一行代码都具有匹配的行来还原它。

答案 1 :(得分:3)

Vucko的答案很好,总的来说(总是更新视口的每个组件)是您绝对应该做的事情。

但是,我想补充一下,似乎您没有正确遵循ViewHolder模式:您的onBindViewHolder()方法永远不要调用findViewById()。相反,您的ViewHolder类应该找到每个视图一次,然后保存对它们的引用。

class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

    val connectorRight: ImageView = itemView.findViewById(R.id.citation_select_connector_right)
    val connectorLeft: ImageView = itemView.findViewById(R.id.citation_select_connector_left)
    // ...
}

然后您可以直接在onBindViewHolder()内部使用这些字段:

if (position % 2 == 0) {
    holder.connectorRight.visibility = View.VISIBLE
    holder.connectorLeft.visibility = View.GONE
    // ...
} else {
    holder.connectorLeft.visibility = View.VISIBLE
    holder.connectorRight.visibility = View.GONE
    // ...
}