I'm facing a layout issue with a vertical RecyclerView whose items use are MaterialCards with custom shapeAppearanceOverlay which has asymmetric corners (top-left corner radius = 0dp, all other corners = 16dp).
When the cards are tall and appear near the bottom of the screen, the outline spot shadow becomes noticeably vertically shifted. I understand this is how Android renders shadows, but the offset becomes too large in this specific case, and produces visual artifacts.
This issue is reproducible in minimal project: Activity + RecyclerView. Just create new project in Android studio and add the code from the sections below:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setupRecycler(generateItems())
}
private fun generateItems(): List<CardAdapter.CardItem> = List(15) { index ->
CardAdapter.CardItem(
title = "Card ${index + 1}",
description = buildString {
repeat((15..40).random()) { append("Test line\n") }
}
)
}
private fun setupRecycler(cardItems: List<CardAdapter.CardItem>) = binding.recyclerView.apply {
layoutManager = LinearLayoutManager(context)
adapter = CardAdapter(cardItems)
val spacingInPixels = resources.getDimensionPixelSize(R.dimen.card_spacing)
addItemDecoration(SpacingItemDecoration(spacingInPixels))
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false" />
</LinearLayout>
class CardAdapter(private val items: List<CardItem>) : RecyclerView.Adapter<CardAdapter.CardViewHolder>() {
data class CardItem(
val title: String,
val description: String
)
inner class CardViewHolder(private val binding: ItemCardBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: CardItem) {
binding.cardTitle.text = item.title
binding.cardDescription.text = item.description
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val binding = ItemCardBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return CardViewHolder(binding)
}
override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount(): Int = items.size
}
<?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:clipChildren="false"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:outlineSpotShadowColor="#FF0000"
app:cardElevation="6dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:shapeAppearanceOverlay="@style/ShapeAppearance.App.Card">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/card_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Card Title"
android:textAppearance="?attr/textAppearanceHeadline6"
android:textStyle="bold" />
<TextView
android:id="@+id/card_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="This is a material card with a minimum height of 200dp"
android:textAppearance="?attr/textAppearanceBody2" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>
<style name="ShapeAppearance.App.Card" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSizeTopLeft">0dp</item>
<item name="cornerSizeTopRight">16dp</item>
<item name="cornerSizeBottomLeft">16dp</item>
<item name="cornerSizeBottomRight">16dp</item>
</style>
What I've tried:
- Replaced MaterialCardView with a regular LinearLayout + shape drawable background → shadow issue still occurs (so it doesn't seem to be a MaterialCardView bug)
- Tested multiple versions of the Material Components library
- Tried XML attributes such as: app:useCompatPadding="true" app:preventCornerOverlap="true" android:clipToPadding="false" / clipToOutline variations
- Experimented with different elevations, padding, and outline settings None of these helped — the shadow still gets pushed downward too far when the view has uneven corner radii.
Question: Is there any known workaround to prevent or reduce this vertical shadow offset when using asymmetric rounded-corner shapes as items in a RecyclerView? Any suggestions or insights would be greatly appreciated.



ViewOutlineProvider. You could replace it with one that sets the same outline but moves the bottom bound. A couple of examples. Since I don't know the cause, the offset was determined by eye, so it's a tad hacky. It will also affect the shadows everywhere on screen, but it's hardly noticeable. There might be other ways; that's just the first that came to mind.