i am a beginner in android studio with kotlin i alwayes struggle in fragment, they are difficult to cope with. i am trying to implement recyclerview in fragment but it does not work i keep getting this error E/RecyclerView: No adapter attached; skipping layout here is my fragment
package com.example.pgm
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
* A simple [Fragment] subclass.
* Use the [VSFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class VSFragment : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val rv = view?.findViewById<RecyclerView>(R.id.activeSubsRecycler)
val subscription = ArrayList<SubscriptionData>()
subscription.add(SubscriptionData("ghassan","1000","2002/1/1","2002/1/3"))
subscription.add(SubscriptionData("ameer","2000","2002/1/1","2002/1/3"))
subscription.add(SubscriptionData("ahmad","9893", "2002/1/1","2002/1/3"))
subscription.add(SubscriptionData("saif","42452", "2002/1/1","2002/1/3"))
subscription.add(SubscriptionData("ghassan","453", "2002/1/1","2002/1/3"))
subscription.add(SubscriptionData("ameer","3354", "2002/1/1","2002/1/3"))
subscription.add(SubscriptionData("ghassan","1000","2002/1/1","2002/1/3"))
subscription.add(SubscriptionData("ameer","2000","2002/1/1","2002/1/3"))
subscription.add(SubscriptionData("ahmad","9893", "2002/1/1","2002/1/3"))
subscription.add(SubscriptionData("saif","42452", "2002/1/1","2002/1/3"))
subscription.add(SubscriptionData("ghassan","453", "2002/1/1","2002/1/3"))
subscription.add(SubscriptionData("ameer","3354", "2002/1/1","2002/1/3"))
rv?.setHasFixedSize(true);
if (rv != null) {
rv.layoutManager = LinearLayoutManager(activity, RecyclerView.VERTICAL, false)
}
if (rv != null) {
rv.adapter = activity?.let { SubscriptionAdapter(it,subscription) }
}
return inflater.inflate(R.layout.fragment_v_s, container, false)
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment VSFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: String, param2: String) =
VSFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
my adapter
package com.example.pgm
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.widget.TextView
import androidx.cardview.widget.CardView
import androidx.recyclerview.widget.RecyclerView
class SubscriptionAdapter (private val context: Context, private val subscriptions : ArrayList<SubscriptionData>):
RecyclerView.Adapter<SubscriptionAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var my_name = itemView.findViewById<TextView>(R.id.tx_name) as TextView
var my_value = itemView.findViewById<TextView>(R.id.tx_value) as TextView
var card_View = itemView.findViewById<CardView>(R.id.cardViewooo) as CardView
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.design2,parent, false)
return ViewHolder(v)
}
override fun getItemCount(): Int {
return subscriptions.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val data = subscriptions[position]
holder.my_name.text = data.name
holder.my_value.text = data.value
holder.card_View.startAnimation(AnimationUtils.loadAnimation(holder.itemView.context,R.anim.main_anim))
holder.itemView.setOnClickListener {
val i = Intent(context,Subscription::class.java)
i.putExtra("name",subscriptions[position].name)
i.putExtra("value",subscriptions[position].value)
i.putExtra("startDate",subscriptions[position].startDate)
i.putExtra("endDate",subscriptions[position].endDate)
context.startActivity(i)
} }
}
XML
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".VSFragment">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@ id/activeSubsRecycler"
/>
</FrameLayout>
what should i do?
CodePudding user response:
onCreateView
is where you're meant to inflate
a layout for your Fragment
to display, and then return it. You're doing that at the end:
return inflater.inflate(R.layout.fragment_v_s, container, false)
But until you do that, the Fragment
doesn't have a view! When you try to access view
at the start, you're actually calling getView()
:
Get the root view for the fragment's layout (the one returned by
onCreateView
), if provided.
So when you do this at the start of onCreateView
:
val rv = view?.findViewById<RecyclerView>(R.id.activeSubsRecycler)
there is no view
, so rv
is null. So all the setup you're trying to do doesn't happen, because you're null-checking rv
before you do them, and since it's null, nothing happens.
When you want to do setup in onCreateView
, you need to inflate your view first, then do all your setup on it, and then return that view:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// get your view inflated
val view = inflater.inflate(R.layout.fragment_v_s, container, false)
// you can make a list like this, if you make your adapter take a
// List<SubscriptionData> instead of an ArrayList<SubscriptionData> (which you should)
val subscription = listOf(
SubscriptionData("ghassan","1000","2002/1/1","2002/1/3"),
SubscriptionData("ameer","2000","2002/1/1","2002/1/3"),
...
)
// Poke around at the stuff in it to get it set up
// This is just a way to null-check once - if it's found (not null),
// this 'let' block will run with the RecyclerView as a variable called 'rv'
view.findViewById<RecyclerView>(R.id.activeSubsRecycler)?.let { rv ->
// don't use an Activity as a context here - just use requireContext,
// you'll have access to one at this point
rv.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
rv.adapter = SubscriptionAdapter(requireContext(),subscription)
}
// now return the inflated view, which you've set up!
return view
}
Get the idea? You inflate a layout that you're going to pass back, but if you need to set up anything on it, you do that before you return it. (You can be smarter in there, using run
or better yet apply
, but if you don't know how that would be more elegant don't worry about it!)
The other option is to override onViewCreated
to handle your setup stuff - so inflate a view in onCreateView
, and that'll get passed into onViewCreated
where you can work with it, do setup etc. Whichever you prefer!