ARTICLE AD BOX
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.


