Move RecyclerView item causes item height to change

Viewed 124

Custom ItemDecoration:

class DemoDecoration(context: Context, left: Float, top: Float, right: Float, bottom: Float) : ItemDecoration() {

    companion object {
        private const val dividerHeight = 8f
        private const val dividerPaddingLeft = 0f
    }

    private val left: Int
    private val top: Int
    private val right: Int
    private val bottom: Int
    private val dividerPaddingRight = 0f
    private val dividerPaint: Paint
    private val textPaint: Paint

    init {
        val density = context.resources.displayMetrics.density
        this.left = (density * left).toInt()
        this.top = (density * top).toInt()
        this.right = (density * right).toInt()
        this.bottom = (density * bottom).toInt()
        dividerPaint = Paint()
        dividerPaint.isAntiAlias = true
        dividerPaint.style = Paint.Style.FILL
        dividerPaint.color = ContextCompat.getColor(context, android.R.color.darker_gray)
        textPaint = Paint()
        textPaint.isAntiAlias = true
        textPaint.style = Paint.Style.FILL
        textPaint.textSize = 30f
        textPaint.typeface = Typeface.DEFAULT_BOLD
        textPaint.color = Color.BLACK
    }

    override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        draw(canvas, parent)
    }

    override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        draw(canvas, parent)
    }

    private fun draw(canvas: Canvas, parent: RecyclerView) {
        if (parent.layoutManager == null) {
            return
        }
        canvas.save()
        drawRect(canvas, parent)
        canvas.restore()
    }

    private fun drawRect(canvas: Canvas, parent: RecyclerView) {
        val childCount = parent.childCount
        val spanCount = getSpanCount(parent)
        val space = 0f.coerceAtLeast(((top + bottom).toFloat() - dividerHeight) / 2)
        var i = 0
        if (i < childCount) {
            val view = parent.getChildAt(i)
            val position = parent.getChildAdapterPosition(view)
            if (position / spanCount % 2 == 0) {
                canvas.drawRect(dividerPaddingLeft, view.bottom + space,
                    parent.right - dividerPaddingRight, view.bottom + space + dividerHeight,
                    dividerPaint)
            }
            i += spanCount
        }
    }

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        if (parent.layoutManager == null) {
            return
        }
        val position = parent.getChildAdapterPosition(view)
        val spanCount = getSpanCount(parent)
        val index = position / spanCount
        if (index > 0 && index % 2 == 0) {
            outRect[left, 56, right] = bottom
        } else {
            outRect[left, top, right] = bottom
        }
    }

    private fun getSpanCount(parent: RecyclerView): Int {
        var spanCount = -1
        val layoutManager = parent.layoutManager
        if (layoutManager is GridLayoutManager) {
            spanCount = layoutManager.spanCount
        } else if (layoutManager is StaggeredGridLayoutManager) {
            spanCount = layoutManager.spanCount
        }
        return spanCount
    }
}

change item position

class MainActivity: AppCompatActivity() {

    private lateinit var change: Button
    private lateinit var recycler: RecyclerView
    private val demoAdapter: DemoAdapter = DemoAdapter()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)

        change = findViewById(R.id.change)
        change.setOnClickListener {
            val list = ArrayList(demoAdapter.currentList)
            list.add(10, list.removeAt(5))
            demoAdapter.submitList(list)
        }

        recycler = findViewById(R.id.recycler)
        recycler.apply {
            layoutManager = GridLayoutManager(this@MainActivity, 5)
            setHasFixedSize(true)
            isNestedScrollingEnabled = false
            itemAnimator = DefaultItemAnimator()
            addItemDecoration(DemoDecoration(this@MainActivity, 2f, 4f, 2f, 4f))
            adapter = demoAdapter
        }

        val data: MutableList<Model> = ArrayList()
        for (i: Int in 0 .. 39) {
            when ((1..2).random()) {
                1 -> data.add(Model("$i\n\na"))
                2 -> data.add(Model("$i\na\nb"))
            }
        }
        demoAdapter.submitList(data)
    }
}

adapter

class DemoAdapter: ListAdapter<Model, DemoAdapter.ViewHolder>(TaskDiffCallback()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false))
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val model = getItem(position)
        holder.text.text = model.text
    }

    class ViewHolder(view: View): RecyclerView.ViewHolder(view) {
        val container: ConstraintLayout
        val text: TextView

        init {
            container = view.findViewById(R.id.container)
            text = view.findViewById(R.id.text)
        }
    }

    private class TaskDiffCallback : DiffUtil.ItemCallback<Model>() {
        override fun areItemsTheSame(oldItem: Model, newItem: Model): Boolean {
            return oldItem == newItem
        }

        override fun areContentsTheSame(oldItem: Model, newItem: Model): Boolean {
            return oldItem == newItem
        }
    }

}

data class Model(val text: String)

item layout

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/shape_stroke_black">

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_gravity="center"
        android:paddingTop="4dp"
        android:paddingBottom="4dp"
        android:background="@android:color/holo_blue_light"
        android:textColor="@android:color/black"
        android:textSize="12sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

RecyclerviewDemo

5-->10 5-->101

Item height is changed. How can to keep item height unchanged?

1 Answers

You can fix the size of the items you create for the recyclerview. Try to give the average size of the incoming data as width height in the design. or in html logic : width: 100vw; height: 100vh; can you try

Related