Home > Back-end >  Why is the RecyclerView loading before the rest Kotlin Android
Why is the RecyclerView loading before the rest Kotlin Android

Time:04-25

Hello I am doing a little app for my homework and I want to display data from my php api on the app. But the Recycler View send this message 2022-04-25 00:41:36.662 13593-13593/net.robcorp.finalapp E/RecyclerView: No adapter attached; skipping layout before the adapter code runs because this message 2022-04-25 00:41:36.907 13593-13593/net.robcorp.finalapp I/System.out: [Drivers(pos=1, name=Charles Leclerc, nb=16, points=71, title=Ferrari), Drivers(pos=2, name=Esteban Ocon, nb=31, points=20, title=Alpine)] is displayed after the previous one and is suppose to run just before the adapter. Here is the DriversFragment.kt:

package net.robcorp.finalapp

import android.content.Context
import android.graphics.Insets.add
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.OneShotPreDrawListener.add
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import net.robcorp.finalapp.R
import net.robcorp.finalapp.databinding.FragmentDriversBinding
import net.robcorp.finalapp.drivers.DriverFragmentAdapter
import net.robcorp.finalapp.drivers.Drivers
import org.json.JSONArray


class DriversFragment : Fragment(R.layout.fragment_drivers) {

    private lateinit var recyclerView: RecyclerView
    private lateinit var myAdapter: DriverFragmentAdapter;
    lateinit var binding: FragmentDriversBinding
    private val drivers = ArrayList<Drivers>()
    val url = "https://robcorp.net/f1api/getdrivers.php"

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding = FragmentDriversBinding.inflate(layoutInflater)
        recyclerView = binding.driversRecyclerView
        recyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
        myAdapter = DriverFragmentAdapter(drivers)
        recyclerView.adapter = myAdapter
        println("adapter loaded")
        downloadDrivers()
    }

    fun downloadDrivers() {
        val task = Volley.newRequestQueue(this.context)
        val request = StringRequest(Request.Method.GET, url, {
                response ->
            val data = response.toString()
            val jArray = JSONArray(data)
//            Log.e("Error",response.toString())
            for (i in 0..jArray.length()-1) {
                val json_data = jArray.getJSONObject(i)
//                Log.e("Jobject",json_data.toString())
                val pos = i 1
                val name = json_data.getString("Name")
                val nb = json_data.getString("NB")
                val points = json_data.getString("Points")
                val title = json_data.getString("Title")
                val driver = Drivers(pos, name, nb, points, title)
                drivers.add(driver)

            }
            println(drivers)
            myAdapter.notifyDataSetChanged()
        }, {
                error ->
            println(error)
        })
        task.add(request)
    }
}

Here is the DriverFragmentAdapter.kt:

package net.robcorp.finalapp.drivers

import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import net.robcorp.finalapp.R
import net.robcorp.finalapp.databinding.DriverListBinding

class DriverFragmentAdapter(private var driversList: List<Drivers>): RecyclerView.Adapter<DriverFragmentAdapter.MyViewHolder>() {

    class MyViewHolder(val binding: DriverListBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(driver: Drivers) {
            val context = itemView.context
            val pos = itemView.findViewById<TextView>(R.id.driver_position)
            val bar = itemView.findViewById<TextView>(R.id.driver_bar)
            val name = itemView.findViewById<TextView>(R.id.driver_name)
            val nb = itemView.findViewById<TextView>(R.id.driver_number)
            val team = itemView.findViewById<TextView>(R.id.driver_team)
            val points = itemView.findViewById<TextView>(R.id.driver_points)

            pos.text = driver.pos.toString()
            name.text = driver.name
            nb.text = driver.nb
            team.text = driver.title
            points.text = driver.points

            if (driver.title == "Ferrari") {
                bar.background = ContextCompat.getDrawable(context, R.drawable.ferrari_bar)
            } else if (driver.title == "Alpine") {
                bar.background = ContextCompat.getDrawable(context, R.drawable.alpine_bar)
            } else if (driver.title == "Red Bull") {
                bar.background = ContextCompat.getDrawable(context, R.drawable.redbull_bar)
            } else if (driver.title == "Mercedes") {
                bar.background = ContextCompat.getDrawable(context, R.drawable.mercedes_bar)
            } else if (driver.title == "McLaren") {
                bar.background = ContextCompat.getDrawable(context, R.drawable.mclaren_bar)
            } else if (driver.title == "Alfa Romeo") {
                bar.background = ContextCompat.getDrawable(context, R.drawable.alfaromeo_bar)
            } else if (driver.title == "AlphaTauri") {
                bar.background = ContextCompat.getDrawable(context, R.drawable.alphatauri_bar)
            } else if (driver.title == "Williams") {
                bar.background = ContextCompat.getDrawable(context, R.drawable.williams_bar)
            } else if (driver.title == "Aston Martin") {
                bar.background = ContextCompat.getDrawable(context, R.drawable.astonmartin_bar)
            } else if (driver.title == "Haas") {
                bar.background = ContextCompat.getDrawable(context, R.drawable.haas_bar)
            }
        }
    }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        return MyViewHolder(DriverListBinding.inflate(LayoutInflater.from(parent.context)))

    }

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

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val driver = driversList[position]
        holder.binding.apply {
            println("je suis dans le binding")
            holder.bind(driver)
        }
    }

    fun setDriversList(driversList: List<Drivers>) {
        this.driversList = driversList
        notifyDataSetChanged()
    }

}

fragment_drivers.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=".MainActivity">

    <!-- TODO: Update blank fragment layout -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <LinearLayout
            android:id="@ id/header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/primary"
            android:orientation="vertical"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginBottom="10dp">

            <TextView
                android:id="@ id/calendar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="30dp"
                android:layout_marginTop="20dp"
                android:layout_marginEnd="30dp"
                android:layout_marginBottom="20dp"
                android:fontFamily="@font/marianneb"
                android:text="Pilotes"
                android:textAlignment="center"
                android:textColor="@color/white"
                android:textSize="25sp" />

        </LinearLayout>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@ id/drivers_recycler_view"
            android:scrollbars="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintTop_toBottomOf="@ id/header"/>

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

driver_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
    android:layout_marginStart="20dp"
    android:layout_marginTop="10dp"
    android:layout_marginEnd="20dp"
    android:elevation="8dp"
    android:orientation="horizontal"
    android:padding="10dp"
    android:background="@drawable/rectangle">

    <TextView
        android:id="@ id/driver_position"
        android:layout_width="30dp"
        android:layout_height="match_parent"
        android:fontFamily="@font/marianneb"
        android:text="1"
        android:textColor="@color/black"
        android:textSize="25sp"
        android:textAlignment="center"/>

    <ImageView
        android:id="@ id/driver_bar"
        android:layout_width="20px"
        android:layout_height="match_parent"
        android:background="@drawable/ferrari_bar"/>



    <LinearLayout
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@ id/driver_name"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:fontFamily="@font/marianneb"
            android:text="Charles Leclerc"
            android:textColor="@color/black"
            android:textSize="15sp"
            android:layout_marginStart="10dp"/>

        <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

            <TextView
                android:id="@ id/driver_number"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="10dp"
                android:layout_marginTop="2.5dp"
                android:fontFamily="@font/mariannem"
                android:text="16"
                android:textColor="@color/black"
                android:textSize="10sp" />

            <TextView
                android:id="@ id/driver_team"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="10dp"
                android:layout_marginTop="2.5dp"
                android:fontFamily="@font/mariannem"
                android:text="Ferrari"
                android:textColor="@color/black"
                android:textSize="10sp" />

        </LinearLayout>

    </LinearLayout>

    <TextView
        android:id="@ id/driver_points"
        android:layout_width="50dp"
        android:layout_height="match_parent"
        android:fontFamily="@font/marianneb"
        android:text="578"
        android:textColor="@color/black"
        android:textSize="25sp"/>



</LinearLayout>

CodePudding user response:

It’s a harmless warning. Since your RecyclerView is already in the layout, it attempts to draw it the moment your view hierarchy becomes visible, sees that there is no adapter assigned, and so it just issues the warning and doesn’t draw it.

Perhaps it would be cleaner to create your adapter (with an empty list) and assign it immediately upon inflating the view, and just update its list when the data is ready.

By the way, it is incorrect to inflate your layout in onCreate. That should be done in onCreateView.


Edit:

Your code here is inflating the layout twice, once for the variable v, and once for the binding property. First of all, there's no reason to inflate it twice. Secondly, since you use v as the return value of onCreateView, anything you do to the views in the binding is pointless and has no effect on the views that are actually on screen.

    val v = inflater.inflate(R.layout.fragment_drivers, container, false)
    binding = FragmentDriversBinding.inflate(layoutInflater)
    // ...
    return v

You should only inflate the view once (using the binding) and return the binding's view:

    // DELETE THIS LINE: val v = inflater.inflate(R.layout.fragment_drivers, container, false)
    binding = FragmentDriversBinding.inflate(layoutInflater)
    // ...
    return binding.root

Also, it is more proper to set up your views in onViewCreated() instead of in onCreateView(). And if you do that, you actually can move inflation into the super-constructor call by passing the view ID to the Fragment constructor, and then instead of inflating your binding, you bind to the existing view. It would look like this:

class DriversFragment : Fragment(R.layout.fragment_drivers) {

    private lateinit var recyclerView: RecyclerView
    private lateinit var myAdapter: DriverFragmentAdapter;
    lateinit var binding: FragmentDriversBinding
    private val drivers = ArrayList<Drivers>()
    val url = "https://robcorp.net/f1api/getdrivers.php"

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding = FragmentDriversBinding.bind(view)
        recyclerView = binding.driversRecyclerView

        recyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)

        myAdapter = DriverFragmentAdapter(drivers)

        recyclerView.adapter = myAdapter
        println("adapter loaded")
        downloadDrivers()
    }

    //...
}

CodePudding user response:

To add a little bit of clarity to @Tenfour04 's answer.

You can't remove this. They are telling you how to properly inflate it with viewbinding

override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
): View? {
    binding = FragmentDriversBinding.inflate(layoutInflater, container, false)
    return binding.root
}

also a little side note:

You should destroy your binding like so to prevent memory leaks

override fun onDestroyView() {
    super.onDestroyView()
    binding = null
}

Now to the question.

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        //binding = FragmentDriversBinding.bind(view) this is not needed since its already been inflated
        //recyclerView = binding.driversRecyclerView why keep recyclerview as another variable when you can access it with binding.driversRecyclerView?

        binding.driversRecyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)

        myAdapter = DriverFragmentAdapter(drivers)

        binding.driversRecyclerView.adapter = myAdapter
        println("adapter loaded")
        downloadDrivers()
    }

Finally the issue is most likely your layout. Since everything is wrapped inside a linear layout, the constraint layouts position lines don't do anything. I removed the useless linear layout. Hopefully this fixes any issues.

sorry about the formatting btw just use CTRL ALT L to auto format it

<?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=".MainActivity">

            <LinearLayout
            android:id="@ id/header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/primary"
            android:orientation="vertical"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginBottom="10dp">

            <TextView
                android:id="@ id/calendar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="30dp"
                android:layout_marginTop="20dp"
                android:layout_marginEnd="30dp"
                android:layout_marginBottom="20dp"
                android:fontFamily="@font/marianneb"
                android:text="Pilotes"
                android:textAlignment="center"
                android:textColor="@color/white"
                android:textSize="25sp" />

        </LinearLayout>

         <androidx.recyclerview.widget.RecyclerView
            android:id="@ id/drivers_recycler_view"
            android:scrollbars="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintTop_toBottomOf="@ id/header"/>

</androidx.constraintlayout.widget.ConstraintLayout>
  • Related