I have overlapping cards and on swiping left I want the top card which is swiped to back to bottom of the all cards, behind the current bottom card. This swiped card should reappear again after the all the cards above it are swiped. Below is the image of my overlapping cards. UI Image
So, initially my activity code is as follows MainActivity.kt
import android.graphics.Canvas
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var adapter:ToLearnAdapter
private val items = mutableListOf("Card 1", "Card 2", "Card 3", "Card 4", "Card 5")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recyclerView)
adapter = ToLearnAdapter(items)
recyclerView.adapter = adapter
recyclerView.layoutManager = OverlappingLayoutManager(this)
val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.adapterPosition
adapter.moveToBottom(position)
}
override fun onChildDraw(
c: Canvas,
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
dX: Float,
dY: Float,
actionState: Int,
isCurrentlyActive: Boolean
) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
viewHolder.itemView.alpha = 1 - Math.abs(dX) / recyclerView.width
viewHolder.itemView.translationX = dX
}
})
itemTouchHelper.attachToRecyclerView(recyclerView)
}
}
Below is my Adapter class ToLearnAdapter.kt
package com.example.overlapping
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class ToLearnAdapter(private val items:MutableList<String>):
RecyclerView.Adapter<ToLearnAdapter.ToLearnViewHolder>(){
class ToLearnViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val textView: TextView = view.findViewById(R.id.textView)
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ToLearnAdapter.ToLearnViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.card_item,parent,false)
return ToLearnViewHolder(view)
}
override fun onBindViewHolder(holder: ToLearnAdapter.ToLearnViewHolder, position: Int) {
holder.textView.text = items[position]
}
override fun getItemCount(): Int {
return items.size
}
fun moveToBottom(position: Int) {
if (position < 0 || position >= items.size) return
val item = items.removeAt(position)
items.add(item)
notifyItemMoved(position, items.size - 1)
notifyItemChanged(items.size - 1)
}
}
Below is my Custom LayoutManager caalled OverlappingLayoutManager.kt
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.view.animation.Interpolator
import android.view.animation.LinearInterpolator
import androidx.annotation.Dimension
import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.RecyclerView
class OverlappingLayoutManager(context: Context) : RecyclerView.LayoutManager() {
private val horizontalOverlap: Float =
context.resources.getDimension(R.dimen.card_horizontal_overlap)
private val verticalOverlap: Float =
context.resources.getDimension(R.dimen.card_vertical_overlap)
@get:Dimension
private val tiltAngle: Float =
context.resources.getDimension(R.dimen.card_tilt_angle)
private var interpolator: Interpolator = LinearInterpolator() // Use an interpolator for smooth tilting
override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
return RecyclerView.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State?) {
if (itemCount == 0) {
removeAndRecycleAllViews(recycler)
return
}
detachAndScrapAttachedViews(recycler)
val parentWidth = width
val parentHeight = height
var zIndex = 0
for (position in 0 until itemCount) {
val view = recycler.getViewForPosition(position)
addView(view)
// Set z-index for the view
val elevation = zIndex.toFloat()
ViewCompat.setElevation(view, elevation)
zIndex++
measureChildWithMargins(view, 0, 0)
val width = getDecoratedMeasuredWidth(view)
val height = getDecoratedMeasuredHeight(view)
val centerX = parentWidth / 2f
val centerY = parentHeight / 2f
val itemOffset = calculateItemOffset(position)
val newCenterX = centerX + itemOffset * horizontalOverlap
val left = (newCenterX - (width / 2)).toInt()
val top = (centerY - (height / 2)).toInt()
val right = (newCenterX + (width / 2)).toInt()
val bottom = (centerY + (height / 2)).toInt()
layoutDecoratedWithMargins(view, left, top, right, bottom)
applyTilt(view, position)
}
}
private fun applyTilt(view: View, position: Int) {
val tilt = calculateTilt(position) * tiltAngle
view.pivotX = (view.width / 2).toFloat()
view.pivotY = (view.height / 2).toFloat()
view.rotation = tilt
}
protected open fun calculateItemOffset(position: Int): Float {
return if (position % 2 == 0) -1f else 1f
}
protected open fun calculateTilt(position: Int): Float {
return if (position % 2 == 0) -1f else 1f
}
fun setInterpolator(interpolator: Interpolator) {
this.interpolator = interpolator
}
}
My layout file activity_main.xml is as follows
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="40dp"
tools:listitem="@layout/card_item" />
</RelativeLayout>
and layout file for adapter class is card_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_margin="18dp"
card_view:cardCornerRadius="8dp"
card_view:cardElevation="4dp">
<TextView
android:layout_gravity="center"
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="24sp"
android:text="Card Text" />
</androidx.cardview.widget.CardView>
So, I tried update the data by creating a separate moveToBottom function in adapter class but still when i try to swipe the front card, it doesn’t go back to the bottom of the cards nor it goes away, it just moves for a moment and comes back to the same position
I have created a separate function in adapater class which has a logic yo move the card to the bottom but the card just doesn’t go to the bottom, when i swipe LEFT, it swipes and comes back to original top position again
Freya19 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.