The solution that I have provided below helps in displaying pre-defined number of child items('numOfCardViewItemsOnScreen') in a recyclerView on the screen, also calculates the margins between these items to space them evenly, by taking into account the supplied margin between the items & recyclerView's end margin's (i.e, 'marginAtRecyclerViewsEnds')
Note that I have tested this in devices between 5.0" to 7.0" inch devices & obtained consistent result.
private fun configureCardViewLayoutParams(cardView: CardView, numOfCardViewItemsOnScreen: Int, marginAtRecyclerViewsEnds: Float, marginLeftParam: Float, marginRightParam: Float, marginTopParam: Float, marginBottomParam: Float): ViewGroup.MarginLayoutParams {
val numOfGapsInBetweenItems = numOfCardViewItemsOnScreen - 1
var combinedGapWidth = ((marginLeftParam + marginRightParam) * numOfGapsInBetweenItems) + (marginAtRecyclerViewsEnds * 2)
//Provided approx. adjustment of 2dp to prevent the extreme edges of the card from being cut-off
combinedGapWidth += 2
val combinedGapWidthDp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, combinedGapWidth, cardView.resources.displayMetrics).toInt()
//Since margins/gaps are provided in-between the items, these have to be taken to account & subtracted from the width in-order to obtain even spacing
cardView.layoutParams.width = (getScreenWidth(cardView.context as MainActivity) - combinedGapWidthDp) / numOfCardViewItemsOnScreen
//Margins in-between the items
val marginLeftOfItem = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, marginLeftParam, cardView.resources.displayMetrics).toInt()
val marginRightOfItem = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, marginRightParam, cardView.resources.displayMetrics).toInt()
val marginTopOfItem = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, marginTopParam, cardView.resources.displayMetrics).toInt()
val marginBottomOfItem = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, marginBottomParam, cardView.resources.displayMetrics).toInt()
//Providing the above margins to the CardView
val cardViewMarginParams: ViewGroup.MarginLayoutParams = cardView.layoutParams as ViewGroup.MarginLayoutParams
cardViewMarginParams.setMargins(marginLeftOfItem, marginTopOfItem, marginRightOfItem, marginBottomOfItem)
return cardViewMarginParams
}
The below method helps in calculating the screenWidth as the name implies
private fun getScreenWidth(activity: MainActivity): Int {
var width: Int = 0
val size = Point()
val windowManager = activity.getSystemService(Context.WINDOW_SERVICE) as WindowManager
width = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val bounds = windowManager.currentWindowMetrics.bounds
bounds.width()
} else {
val display: Display? = windowManager.defaultDisplay
display?.getSize(size)
size.x
}
return width
}
Finally inside onCreateViewHolder, utilise the configureCardViewLayoutParams method depicted above & pass in the desired arguments
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyRecyclerViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.row_item_layout, parent, false)
view.layoutParams = configureCardViewLayoutParams(view as CardView, 4, 12f, 4f, 4f, 8f, 8f)
return MyRecyclerViewHolder(view)
}
For the sake of quick understanding, I have also provided the child items xml layout (row_item_layout.xml) So as you can see from the below xml, the root view of our child item layout is a CardView.
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="5dp"
android:foregroundGravity="center">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView1"
android:layout_width="30dp"
android:layout_height="28dp"
android:layout_marginTop="22dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginTop="18dp"
android:layout_marginEnd="7dp"
android:layout_marginBottom="16dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="2"
android:text="@{accountOptionsModel.title}"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView1"
tools:text="@tools:sample/full_names" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
There is no special logic within the getCount(), just return the entire list size
override fun getItemCount(): Int = myList.size
Below screenshots are from devices of 5.0", 6.3", 7.02" respectively

