Noah Di Gesu
Noah Di Gesu

Reputation: 55

RecyclerView functions aren't called

I am trying to show a list of transactions in a recycler view.
Each transaction is represented by a CardView inside a constraint layout (see item_transaction.xml).

Somehow, the functions of my RecyclerView Adapter (onCreateViewHolder, onBindViewHolder and getItemCount) are never called (logs are never displayed - I removed most of them so the code is easier to read). Therefore, the RecyclerView content doesn't display on my app.

It is also worth pointing out that I have a list of transactions called data in my adapter. Whenever I set the data in the list, it does update. No issues there.

💡 I know notifyDataSetChanged() isn't clean, I'll change it later on when I manage to get everything working.

Here is a preview of what I want so you can grasp the idea better : Preview

EDIT : My fragment displays normally and has no problem executing code.

Hang on tight because there is a lot of code :

package com.example.manage.manageit.database

class Transaction(
    var value: Long = 0,
    var note: String = "",
    var budget: String = "",
    var creationTime: Long = System.currentTimeMillis()
)

Transaction.kt

package com.example.manage.manageit.home

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.example.manage.manageit.databinding.ActivityHomeBinding

import androidx.fragment.app.Fragment
import com.example.manage.manageit.R
import com.example.manage.manageit.about.AboutFragment
import com.example.manage.manageit.budget.TransactionFragment

class HomeActivity : AppCompatActivity() {
    private lateinit var binding: ActivityHomeBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_home)

        binding.bottomNavigation.menu.getItem(0).isCheckable = true
        setFragment(HomeFragment())

        binding.bottomNavigation.setOnItemSelectedListener {menu ->
            when(menu.itemId){

                R.id.homeFragmentButton -> {
                    setFragment(HomeFragment())
                    true
                }

                R.id.transactionFragmentButton -> {
                    setFragment(TransactionFragment())
                    true
                }

                R.id.aboutFragmentButton -> {
                    setFragment(AboutFragment())
                    true
                }

                else -> false
            }
        }

    }

    private fun setFragment(fr : Fragment){
        val frag = supportFragmentManager.beginTransaction()
        frag.replace(R.id.myNavHostFragment,fr)
        frag.commit()
    }
}

HomeActivity.kt : It contains a BottomNavigationView and my fragment.

package com.example.manage.manageit.home

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.manage.manageit.R
import com.example.manage.manageit.adapters.TransactionAdapter
import com.example.manage.manageit.database.Transaction
import com.example.manage.manageit.databinding.FragmentHomeBinding
import java.util.logging.Logger

class HomeFragment : Fragment() {
    private var logger = Logger.getLogger(HomeFragment::class.java.name)

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val binding: FragmentHomeBinding = DataBindingUtil.inflate(
            inflater,
            R.layout.fragment_home,
            container,
            false
        )

        val recyclerView: RecyclerView = binding.transactionList
        val transactionAdapter = TransactionAdapter()
        recyclerView.adapter = transactionAdapter
        recyclerView.layoutManager = LinearLayoutManager(context)

        transactionAdapter.data = listOf(
            Transaction(10, "Transaction note", "Groceries"),
            Transaction(20, "Transaction note", "Groceries"),
            Transaction(30, "Transaction note", "Groceries")
        )

        return inflater.inflate(R.layout.fragment_home, container, false)
    }
}

HomeFragment.kt

package com.example.manage.manageit.adapters

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.manage.manageit.R
import com.example.manage.manageit.database.Transaction
import java.util.logging.Logger

class TransactionAdapter : RecyclerView.Adapter<TransactionViewHolder>() {
    private var logger = Logger.getLogger(this::class.java.name)

    var data = listOf<Transaction>()
        set(value) {
            field = value
            notifyDataSetChanged()
        }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionViewHolder {
        logger.info("adapter : onCreateViewHolder()")
        return TransactionViewHolder.from(parent)
    }

    override fun onBindViewHolder(holder: TransactionViewHolder, position: Int) {
        val item = this.data[position]
        holder.bind(item)
    }

    override fun getItemCount(): Int {
        logger.info("adapter : getItemCount()")
        return this.data.size
    }
}

class TransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val icon: ImageView = itemView.findViewById(R.id.transaction_icon)
    private val note: TextView = itemView.findViewById(R.id.transaction_note)
    private val value: TextView = itemView.findViewById(R.id.transaction_value)

    fun bind(item: Transaction) {
        icon.setImageResource(R.drawable.ic_dollar_sign)
        note.text = item.note
        value.text = item.value.toString()
    }

    companion object {
        fun from(parent: ViewGroup): TransactionViewHolder {
            val layoutInflater = LayoutInflater.from(parent.context)
            val view = layoutInflater
                .inflate(R.layout.item_transaction, parent, false)

            return TransactionViewHolder(view)
        }
    }
}

TransactionAdapter.kt

<?xml version="1.0" encoding="utf-8"?>
<layout 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"
    tools:context="com.example.manage.manageit.home.HomeActivity">

    <RelativeLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <androidx.fragment.app.FragmentContainerView
                android:id="@+id/myNavHostFragment"
                android:name="androidx.navigation.fragment.NavHostFragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:defaultNavHost="true"/>
        </LinearLayout>

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottom_navigation"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:background="@android:color/black"
            android:paddingVertical="6dp"
            app:itemIconSize="28dp"
            app:itemIconTint="@color/nav_bar_item"
            app:itemRippleColor="@color/ripple_color"
            app:itemTextColor="@color/nav_bar_item"
            app:labelVisibilityMode="unlabeled"
            app:menu="@menu/bottom_navigation_menu" />

    </RelativeLayout>
</layout>

activity_home.xml

<?xml version="1.0" encoding="utf-8"?>

<layout 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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/black"
        android:fillViewport="true"
        tools:context="com.example.manage.manageit.home.HomeFragment">

        <TextView
            android:id="@+id/homeActivityTitle"
            style="@style/activityTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/medium_margin"
            android:layout_marginTop="@dimen/medium_margin"
            android:layout_marginEnd="@dimen/medium_margin"
            android:text="@string/home_text"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/transaction_list"
            android:layout_width="match_parent"
            android:layout_height="400dp"
            android:visibility="visible"
            app:layout_constraintBottom_toTopOf="@+id/floatingActionButton"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/homeActivityTitle" />

        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/floatingActionButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="@dimen/medium_margin"
            android:layout_marginBottom="@dimen/very_big_margin"
            android:backgroundTint="@color/secondaryColor"
            android:clickable="true"
            android:contentDescription="@string/add_button_description"
            android:src="@drawable/ic_plus"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:maxImageSize="32dp"
            tools:ignore="RedundantDescriptionCheck" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

fragment_home.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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.cardview.widget.CardView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="20dp"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="20dp"
        app:cardBackgroundColor="@android:color/transparent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:background="@drawable/transaction">

            <ImageView
                android:id="@+id/transaction_icon"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="30dp"
                android:contentDescription="Icon"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/ic_dollar_sign"
                app:tint="@android:color/white" />

            <TextView
                android:id="@+id/transaction_note"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="30dp"
                android:text="Transaction note"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toEndOf="@+id/transaction_icon"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/transaction_value"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="30dp"
                android:text="55€"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
        </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

item_transaction.xml : Basically a card view with an image and two text views (the transaction "note" and value).

Upvotes: 0

Views: 82

Answers (1)

Tenfour04
Tenfour04

Reputation: 93531

In onCreateView, you have created your view layout twice. The first one you did using DataBindingUtil and that's the one whose RecyclerView you set up. But then you let that whole layout go back to the garbage collector because you create a brand new layout using layoutInflater and return that layout on the last line of onCreateView.

Technically, you should not be setting up views in onCreateView anyway. It should be done in onViewCreated(), although I don't think it makes much difference. However, since Fragment provides a constructor that can automatically inflate a provided layout ID, I think it's cleaner anyway to eliminate onCreateView() entirely, like this:

class HomeFragment : Fragment(R.layout.fragment_home) {
    private var logger = Logger.getLogger(HomeFragment::class.java.name)

    override fun onViewCreated(
        view: View,
        savedInstanceState: Bundle?
    ) {
        val binding: FragmentHomeBinding = DataBindingUtil.bind(view)

        val recyclerView: RecyclerView = binding.transactionList
        val transactionAdapter = TransactionAdapter()
        recyclerView.adapter = transactionAdapter
        recyclerView.layoutManager = LinearLayoutManager(context)

        transactionAdapter.data = listOf(
            Transaction(10, "Transaction note", "Groceries"),
            Transaction(20, "Transaction note", "Groceries"),
            Transaction(30, "Transaction note", "Groceries")
        )
    }
}

Upvotes: 1

Related Questions