When first time recyclerView
Load It will show all proper. but after scrolling, layout item size changed.
this is my item_layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="@drawable/item_border"
android:layout_margin="@dimen/item_margin_2">
<ImageView
android:id="@+id/imageProduct"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/app_name"
android:scaleType="centerInside"
android:src="@drawable/ic_launcher_foreground"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
<TextView
android:id="@+id/textProductTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:textSize="@dimen/title_textSize"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/imageProduct"
tools:text="TubeLight" />
<TextView
android:id="@+id/textProductDetailSort"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textColor="@android:color/black"
android:textSize="@dimen/detail_textSize"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textProductTitle"
tools:text="warranty: 100 days" />
<LinearLayout
android:id="@+id/linerCartItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textProductDetailSort">
<ImageButton
android:id="@+id/buttonRemove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/item_border_small"
android:src="@drawable/ic_outline_remove_24"/>
<TextView
android:id="@+id/textNumberItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorPrimaryLight"
android:textSize="@dimen/detail_small_textSize"
android:padding="@dimen/item_padding"
android:textColor="@color/black"
android:layout_margin="@dimen/item_margin_3"
android:text="0"/>
<ImageButton
android:id="@+id/buttonAdd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/item_border_small"
android:src="@drawable/ic_baseline_add_24"/>
</LinearLayout>
<TextView
android:id="@+id/textViewProductPrice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/linerCartItem"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="@dimen/item_margin"
android:text="@string/title_price"/>
</androidx.constraintlayout.widget.ConstraintLayout>
this is adapter class
class ProductListAdapter(context: Context, private val cellClickListener: HomeFragment) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
interface CellClickListener {
fun onCellClickListener(productModel: ProductModel)
fun onNextClicked()
fun onPrevClicked()
fun onAddClicked(productId: String): Int
fun onRemoveClicked(productId: String): Int
}
val FOOTER_TYPE : Int = 1
val HEADER_TYPE : Int = 2
var context : Context = context
private var listOfProducts = listOf<ProductModel>()
override fun getItemViewType(position: Int): Int {
if (position == listOfProducts.size) {
return FOOTER_TYPE
}
// else if(position == 0){
// return HEADER_TYPE
// }
return 0
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == FOOTER_TYPE){
return ProductListViewHolderFooter(
LayoutInflater.from(parent.context).inflate(
R.layout.item_product_footer,
parent, false
)
)
}else if(viewType == HEADER_TYPE){
return ProductListViewHolderHeader(
LayoutInflater.from(parent.context).inflate(
R.layout.item_product_header,
parent, false
)
)
}
else{
return ProductListViewHolder(
context,
LayoutInflater.from(parent.context).inflate(
R.layout.item_product,
parent,
false
)
)
}
}
override fun getItemCount(): Int = listOfProducts.size + 1
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
try {
if (viewHolder is ProductListViewHolder) {
val vh: ProductListViewHolder = viewHolder as ProductListViewHolder
vh.bindView(listOfProducts[position], cellClickListener)
} else if (viewHolder is ProductListViewHolderFooter) {
val vh: ProductListViewHolderFooter = viewHolder as ProductListViewHolderFooter
vh.bindViewFooter(listOfProducts[position], cellClickListener)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
fun setProductList(listOfProducts: List<ProductModel>) {
this.listOfProducts = listOfProducts
notifyDataSetChanged()
}
}
and this is holder
class ProductListViewHolder(context: Context,itemView: View) : RecyclerView.ViewHolder(itemView){
var sharedPreferences: SharedPreferences = context.getSharedPreferences(utils.PREFERENCE_FILE_NAME,
Context.MODE_PRIVATE)
fun bindView(productModel: ProductModel, cellClickListener: HomeFragment) {
itemView.textProductTitle.text = productModel.productTitle
itemView.textProductDetailSort.text = productModel.productDetailSort
itemView.textViewProductPrice.text = context.getString(R.string.title_price) + ": " + productModel.price
itemView.textNumberItem.text = cart
Glide.with(itemView.context).load(productModel.productImageUrl!!).into(itemView.imageProduct)
itemView.imageProduct.setOnClickListener{
cellClickListener.onCellClickListener(productModel)
}
itemView.buttonRemove.setOnClickListener {
val cart = cellClickListener.onRemoveClicked(productModel.productId)
itemView.textNumberItem.text = cart.toString()
}
itemView.buttonAdd.setOnClickListener {
val cart = cellClickListener.onAddClicked(productModel.productId)
itemView.textNumberItem.text = cart.toString()
}
}
}
The problem caused by not having any control over the height of the ImageView
which leads to varying sizes in the view-recycling process. To fix this issue, you need to apply a bit of change in the imageProduct
attributes.
First, as you bound the imageProduct
's start and end to the parent's start and end, you should set the view width to 0dp
to be sure it fills the whole space.
Second, set adjustViewBounds
attribute to true
. It leads to adjusting the ImageView
bounds to preserve the aspect ratio of its drawable.
Third, set layout_constrainedHeight
attribute to true
. It ensures that the height of the view remains constrained and also lets view height remains as wrap_content
.
<ImageView
android:id="@+id/imageProduct"
android:layout_width="0dp" // 1
android:layout_height="wrap_content"
android:contentDescription="@string/app_name"
android:scaleType="centerInside"
android:src="@drawable/ic_launcher_foreground"
android:adjustViewBounds="true" // 2
app:layout_constrainedHeight="true" // 3
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
In addition, if you want to always show the image with a specific aspect ratio, you'd be able to achieve it by changing the height to 0dp
also defining the ratio using layout_constraintDimensionRatio
attribute, like the following:
<ImageView
android:id="@+id/imageProduct"
android:layout_width="0dp"
android:layout_height="0dp" // setting the height constrainted
android:contentDescription="@string/app_name"
android:scaleType="centerInside"
android:src="@drawable/ic_launcher_foreground"
android:adjustViewBounds="true"
app:layout_constraintDimensionRatio="1:1" // to show as a square
app:layout_constrainedHeight="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />