Home > database >  How can I pass the fragment context to the adapter?
How can I pass the fragment context to the adapter?

Time:06-13

I am working on a project and stuck on this for 3 days now. The project reads a json, using an adapter, and fragments.

json: https://opendata.visitflanders.org/accessibility/activities/sport_v2.json

fragment_list_of_sports.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="match_parent"
    tools:context=".ListOfSportsFragment">

    <!-- TODO: Update blank fragment layout -->


    <TextView
        android:id="@ id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="List of sports and locations"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.022" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@ id/rvSportList"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:id="@ id/btnBack"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Back"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.049"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.976" />
</androidx.constraintlayout.widget.ConstraintLayout>

ListOfSportsFragment.kt:

package vives.be.bedoerikproject

import SportsAdapter
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.get
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.fragment_list_of_sports.*
import kotlinx.android.synthetic.main.fragment_list_of_sports.view.*
import org.json.JSONException
import org.json.JSONObject

class ListOfSportsFragment : Fragment() {

     override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_list_of_sports, container, false)

        val sportList: ArrayList<SportModelClass> = ArrayList()

        try {
            // As we have JSON object, so we are getting the object
            //Here we are calling a Method which is returning the JSON object
            val obj = JSONObject("Sports.json")
            // fetch JSONArray named users by using getJSONArray
            val sportsArray = obj.getJSONArray("sports")
            // Get the users data using for loop i.e. id, name, email and so on

            for (i in 0 until sportsArray.length()) {
                // Create a JSONObject for fetching single User's Data
                val sport = sportsArray.getJSONObject(i)
                // Fetch id store it in variable
                val id = sport.getInt("business_product_id")
                val name = sport.getString("name")
                val city_name = sport.getString("city_name")
                val postal_code = sport.getInt("postal_code")
                val website = sport.getString("website")
                val phone = sport.getString("phone1")
                val email = sport.getString("email")

                // Now add all the variables to the data model class and the data model class to the array list.
                val sportDetails =
                    SportModelClass(id, name, postal_code, city_name, phone, email, website)

                // add the details in the list
                sportList.add(sportDetails)
            }
        } catch (e: JSONException) {
            //exception
            e.printStackTrace()
        }

       // Set the LayoutManager that this RecyclerView will use.
        rvSportList.layoutManager = LinearLayoutManager(activity) //rv = reciycleview
        // Adapter class is initialized and list is passed in the param.
        val itemAdapter = SportsAdapter(requireContext(), sportList)
        // adapter instance is set to the recyclerview to inflate the items.
        rvSportList.adapter = itemAdapter

        return view
    }
}

SportsAdapter:

import vives.be.bedoerikproject.SportModelClass
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import vives.be.bedoerikproject.R
import kotlinx.android.synthetic.main.sport_model_layout.view.*

class SportsAdapter(val context: Context, val items: ArrayList<SportModelClass>) :
    RecyclerView.Adapter<SportsAdapter.ViewHolder>() {
    /**
     * Inflates the item views which is designed in xml layout file
     *
     * create a new
     * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
     */
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
            LayoutInflater.from(context).inflate(
                R.layout.sport_model_layout,
                parent,
                false
            )
        )
    }

    /**
     * Binds each item in the ArrayList to a view
     *
     * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
     * an item.
     *
     * This new ViewHolder should be constructed with a new View that can represent the items
     * of the given type. You can either create a new View manually or inflate it from an XML
     * layout file.
     */
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {

        val item = items.get(position)
        holder.name.text = item.name
        holder.city_name.text = item.city_name
        holder.postal_code.text = item.postal_code.toString()
        holder.website.text = item.website
        holder.phone1.text = item.phone1
        holder.email.text = item.email
    }

    /**
     * Gets the number of items in the list
     */
    override fun getItemCount(): Int {
        return items.size
    }

    /**
     * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
     */
    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        // Holds the TextView that will add each item to
        val name = view.name
        val city_name = view.city_name
        val postal_code = view.postal_code
        val website = view.website
        val phone1 = view.phone1
        val email = view.email
    }
}

SportModelClass.kt:

package vives.be.bedoerikproject

class SportModelClass (
    val business_product_id : Int,
    val name : String,
    val postal_code : Int,
    val city_name : String,
    val phone1 : String,
    val email : String,
    val website : String
    )

And I get an error in ListOfSportsFragment in line 60, this one: E/AndroidRuntime: FATAL EXCEPTION: main Process: vives.be.bedoerikproject, PID: 7302 java.lang.NullPointerException: Attempt to invoke virtual method 'androidx.recyclerview.widget.RecyclerView$LayoutManager androidx.recyclerview.widget.RecyclerView.getLayoutManager()' on a null object reference at vives.be.bedoerikproject.ListOfSportsFragment.onCreateView(ListOfSportsFragment.kt:60) at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2995) at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:523) at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1840) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1764) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1701) at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:488) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:223) at android.app.ActivityThread.main(ActivityThread.java:7656) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) Disconnected from the target VM, address: 'localhost:52495', transport: 'socket'

CodePudding user response:

Use requireActivity() like this:

 rvSportList.layoutManager = LinearLayoutManager(requireActivity())

CodePudding user response:

Could you try move your codes from onCreateView to onViewCreated fun on your fragment and use 'context' instead of 'requireContext()'. Hope it hepls.

CodePudding user response:

You can use synthetic view binding like this only after your fragment onCreateView() has returned.

Move the view setup code using synthetics such as rvSportList.layoutManager = LinearLayoutManager(activity) //rv = reciycleview to e.g. onViewCreated().

Also worth noting that the kotlin-android-extensions plugin that provides synthetic view binding is deprecated. Consider using view binding instead.

CodePudding user response:

This is your error:

java.lang.NullPointerException: Attempt to invoke virtual method
'androidx.recyclerview.widget.RecyclerView$LayoutManager
androidx.recyclerview.widget.RecyclerView.getLayoutManager()'
on a null object reference at
vives.be.bedoerikproject.ListOfSportsFragment.onCreateView(ListOfSportsFragment.kt:60)

It's trying to call a method (getLayoutManager()) on something that's supposed to be a RecyclerView, but it's not - it's null. Can't call a method on a null object, so you get the NullPointerException

This is line 60, where the log tells you the error is happening:

rvSportList.layoutManager = LinearLayoutManager(activity) //rv = reciycleview

So if the problem is calling methods on a null RecyclerView, and it's happening on that line, then rvSportList isn't a RecyclerView, it's null.


You're getting that magic rvSportList variable from the Kotlin synthetic extensions you're using, which have been deprecated for a long time. You should use View Binding instead - but in the meantime, try this - the basic way to find a view:

val rvSportList = view.findViewById<RecyclerView>(R.id.rvSportList)
rvSportList.layoutManager = LinearLayoutManager(requireContext())
// etc
  • Related