Search code examples
androidkotlinandroid-layoutandroid-recyclerviewandroid-constraintlayout

Recyclerview item increase height when fetch data again Android Kotlin


I have recyerlView. I loaded data from the server. When it initially loads it height is normal, but when I go to the next activity through item click. And come back it slowly increasing the height of the child. I tried to debug this and found that onResume api call causing the issue. But What I am doing wrong in layout I don't get it.

FirstLayout.xml

<?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:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/reyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

FirstActivity.kt

class FirstActivity : BaseActivity() {

    lateinit var binding: FirstLayoutActivityLayoutBinding
    private val viewModel: FirstViewModel by inject()
    private var listAdapter: ListAdapter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setupViewModel()
        binding = FirstLayoutActivityLayoutBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }

    private fun setupViewModel() {
        viewModel.livedata.observe(this, { list ->
            setupAdapter(list)
        })
    }

    private fun setupAdapter(list: List<String>) {
        initializeAdapter(list)
        listAdapter?.updateItemsList(list)
        binding.recyclerView.apply {
            addItemDecoration(HeaderItemDecoration(context))
             val itemDecorator = DividerItemDecoration(context, DividerItemDecoration.VERTICAL)
        itemDecorator.setDrawable(ContextCompat.getDrawable(context, R.drawable.divider)!!)
        addItemDecoration(itemDecorator)
            adapter = listAdapter
        }
    }

    private fun initializeAdapter(list: List<String>) {
        listAdapter = ListAdapter(list.toMutableList())
    }

    override fun onResume() {
        super.onResume()
        viewModel.fetchItem() // noraml retrofit call
    }
}

HeaderItemDecoration is used from this answer.

ListAdapter.kt

class ListAdapter(private val list: MutableList<String>) : RecyclerView.Adapter<Adapter.MyViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        return MyViewHolder(
            ListLayoutBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
        )
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.bindItem(list[position])
    }

    override fun getItemCount(): Int {
        return list.size
    }

    inner class MyViewHolder(private val binding: ListLayoutBinding) : RecyclerView.ViewHolder(binding.root) {

        fun bindItem(s: String) {
            binding.cool.text = s
        }
    }
}

ListLayout.xml

<?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:id="@+id/root"
    android:background="@color/red"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/cool"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

I am adding my view to my Youtube Link. Please look at what the issue is. Thanks

I didn't add any logic to increase the height

enter image description here


Solution

  • looks like a problem with multiple ItemDecorations, which are set every time new data is fetched. to be exact: multiple DividerItemDecorations, which adds small padding to the items (for dividing them). note methods name starts with add..., so every time you are adding new, old stays there and every instance of this divider is adding some padding. if you would implement e.g. pull-to-refresh or auto-refresh in background every e.g. 10 secs then every refresh GUI method call (setupAdapter) would add some space without leaving Activity. currently you are fetching data only once, in onResume, so every move-activity-to-foreground action will add one divider (when data will be fetched properly)

    move this part of code to onCreate for setting dividers only once, thats proper place for some additional styling by code

    binding.recyclerView.apply {
            addItemDecoration(HeaderItemDecoration(context))
            val itemDecorator = DividerItemDecoration(context, DividerItemDecoration.VERTICAL)
            itemDecorator.setDrawable(ContextCompat.getDrawable(context, R.drawable.divider)!!)
            addItemDecoration(itemDecorator)
    }
    

    and inside your setupAdapter method set only adapter to RecyclerView, don't style it (multiple times)

    private fun setupAdapter(list: List<String>) {
        initializeAdapter(list)
        listAdapter?.updateItemsList(list)
        binding.recyclerView.adapter = listAdapter
    }