I am trying to display items on a recyclerview but it says
"No adapter attached; skipping layout"
.
I can't figure out the error. I tried with Activities instead of Fragements and it works super well. I am not yet so familiar with Framents. Kindly help.
RecyclerAdapter
package com.manzugerald.shukuruyesu.adapter
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.manzugerald.shukuruyesu.HymnDetailActivity
import com.manzugerald.shukuruyesu.R
import com.manzugerald.shukuruyesu.model.Hymns
class HymnsItemAdapter(
val context: Context,
val dataset: List<Hymns>
) : RecyclerView.Adapter<HymnsItemAdapter.ItemViewHolder>() {
//provide a reference to the views for each data item (in this case there is only one item)
//Complex data items may need more than one view per item, and
//you provide access to all the views for a data item in a view holder
//Each data item is just an Affirmation object
inner class ItemViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
//val hymn_imageView: ImageView =view.findViewById(R.id.item_image)
val hymn_title: TextView = view.findViewById(R.id.textView_hymn_title)
val hymn_language: TextView = view.findViewById(R.id.textView_hymn_language)
val hymn_number: TextView = view.findViewById(R.id.textView_hymn_number)
var hymn_detail: TextView = view.findViewById(R.id.textView_hymn_text)
//This has been unused. I will try to see how best to use it in the future
var hymnPosition = 0
/**
* //So as the contents of the views displayed are clickable, append the setOnClickListener to the view holder rather than individual views
* An explicit intent is used for exchanging information between two or more fragements or activities (screen)
* The information to be carried to the next screen is passed as a key-value pair
* The key is retrieved in the receiving activity or fragment
* Then the data the key carries is gotten and assigned to the views so as to be viewed
*/
init {
view.setOnClickListener {
val intent = Intent(context,HymnDetailActivity::class.java)
val message = hymn_number.text.toString()
val message_two = hymn_language.text.toString()
val message_three = hymn_title.text.toString()
val message_four = hymn_detail.text.toString()
intent.putExtra("key_one", message)
intent.putExtra("key_two",message_two)
intent.putExtra("key_three",message_three)
intent.putExtra("key_four",message_four)
context.startActivity(intent)
}
}
}
/**
* Create new views (invoked by the layout manager)
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val adapterLayout = LayoutInflater.from(parent.context).inflate(R.layout.list_item_hymn_list, parent, false)
return ItemViewHolder(adapterLayout)
}
/**
* Replace the contents of a view (invoked by the layout manager)
*/
/**
* Binds or attaches data to the views.
* Does this by interacting with the itemViewHolder
*/
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val item = dataset[position]
//holder.hymn_imageView.setImageResource(item.imageResourceId)
holder.hymn_title.text = context.resources.getString(item.titleStringResourceId)
holder.hymn_language.text = context.resources.getString(item.languageStringResourceId)
holder.hymn_number.text = "Song Number: " + context.resources.getString(item.numberStringResourceId)
holder.hymn_detail.text = context.resources.getString(item.detailStringResourceId)
holder.hymnPosition = position
holder.hymn_detail.isVisible=false
}
/**
* Return the size of the dataset (invoked by the layout manager)
*/
//Takes not of the size of the data, number of rows so that binding can be done with ease
override fun getItemCount(): Int = dataset.size
}
The data source:
package com.manzugerald.shukuruyesu.data
import com.manzugerald.shukuruyesu.R
import com.manzugerald.shukuruyesu.model.Hymns
class HymnsDataSource {
//Gets the data from the string resources
fun loadHymns():List<Hymns>{
return listOf<Hymns>(
Hymns(R.string.affirmation1,R.string.affirmationNum1,R.string.affirmationLang1,R.string.affirmationDetail1),
Hymns(R.string.affirmation2,R.string.affirmationNum2,R.string.affirmationLang2,R.string.affirmationDetail2),
Hymns(R.string.affirmation3,R.string.affirmationNum3,R.string.affirmationLang3,R.string.affirmationDetail3),
Hymns(R.string.affirmation4,R.string.affirmationNum4,R.string.affirmationLang4,R.string.affirmationDetail4),
Hymns(R.string.affirmation5,R.string.affirmationNum5,R.string.affirmationLang5,R.string.affirmationDetail5),
Hymns(R.string.affirmation6,R.string.affirmationNum6,R.string.affirmationLang6,R.string.affirmationDetail6),
Hymns(R.string.affirmation7,R.string.affirmationNum7,R.string.affirmationLang7,R.string.affirmationDetail7),
Hymns(R.string.affirmation8,R.string.affirmationNum8,R.string.affirmationLang8,R.string.affirmationDetail8),
Hymns(R.string.affirmation9,R.string.affirmationNum9,R.string.affirmationLang9,R.string.affirmationDetail9),
Hymns(R.string.affirmation10,R.string.affirmationNum10,R.string.affirmationLang10,R.string.affirmationDetail10)
)
}
}
The Data class:
package com.manzugerald.shukuruyesu.model
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
data class Hymns(
#StringRes var titleStringResourceId: Int,
#StringRes var numberStringResourceId: Int,
#StringRes var languageStringResourceId: Int,
#StringRes var detailStringResourceId: Int
// #DrawableRes val imageResourceId: Int
)
The Fragment to display the recyclerview:
package com.manzugerald.shukuruyesu
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.manzugerald.shukuruyesu.data.HymnsDataSource
import com.manzugerald.shukuruyesu.databinding.FragmentHymnListBinding
import com.manzugerald.shukuruyesu.adapter.HymnsItemAdapter
import com.manzugerald.shukuruyesu.model.Hymns
// 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 [HymnListFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class HymnListFragment : Fragment() {
// TODO: Rename and change types of parameters
//enable and refrence binding
private var _binding: FragmentHymnListBinding? = null
private val binding get() = _binding!!
//property for the recycler view
private lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
//To inflate the layout, call teh onCreatView Method
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentHymnListBinding.inflate(inflater,container,false)
val view = binding.root
return view
}
//Bind the views in onViewCreated
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val myDataSource = HymnsDataSource().loadHymns()
recyclerView = binding.recyclerView
recyclerView.apply {
layoutManager=LinearLayoutManager(requireContext())
adapter= HymnsItemAdapter(dataset = myDataSource, context = requireContext())
adapter= HymnsItemAdapter(context,myDataSource)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding=null
}
}
HymnListFragment Layout File:
<?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=".HymnListFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_View"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="#layout/list_item_hymn_list"/>
</androidx.constraintlayout.widget.ConstraintLayout>
I got the solution to the question...
Neither the Adapter nor the Fragments had a problem.
The problem was in my MainActivity.kt file, which I didn't upload here...
The error came about when I tried changing from activities to Fragments....
Initially, I forgot to take off or comment the inflation of the layouts (Layouts are inflated in the respective Fragments, not the main activity).
For illustration purposes, I will comment the said error in the code.
My bad!
Main Activity class:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(R.layout.activity_main)
}
}
Related
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'
You can set your layout manager in xml file.
...
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvSportList"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
...
and you don't have to pass context into your adapter, you can reach context in onCreateViewHolder like this
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.sport_model_layout,
parent,
false
)
)
}
Could you try move your codes from onCreateView to onViewCreated fun on your fragment and use 'context' instead of 'requireContext()'. Hope it hepls.
Use requireActivity() like this:
rvSportList.layoutManager = LinearLayoutManager(requireActivity())
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.
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
Can someone help me on how to transfer data from one recyclerview fragment to another recyclerview fragment?
This is my newly created CartRecyclerAdapter.kt for my cart recyclerview from a fragment. The main idea of SubmitItem() is to accept the selected item in the Itemrecyclerview.
package com.example.karawapplication
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.example.karawapplication.models.ItemPost
import kotlinx.android.synthetic.main.layout_item_cart_list.view.*
import kotlinx.android.synthetic.main.layout_item_list_item.view.*
class CartRecyclerAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var Items : List<ItemPost> = emptyList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ItemRecyclerAdapter.ItemViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.layout_item_cart_list, parent, false)
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when(holder)
{
is ItemRecyclerAdapter.ItemViewHolder ->
{
holder.bind(Items.get(position))
}
}
}
override fun getItemCount(): Int {
return Items.size
}
fun SubmitItem(Item : ItemPost)
{
Items.toMutableList().add(Item)
}
class ItemViewHolder constructor(
Itemview : View
): RecyclerView.ViewHolder(Itemview)
{
val ItemImage : ImageView = Itemview.Item_image_cart
val ItemTitle : TextView = Itemview.Item_title_cart
val ItemCategory: TextView = Itemview.Item_category_cart
val ItemPrice : TextView = Itemview.Item_price_cart
fun bind (itempost : ItemPost)
{
ItemTitle.setText(itempost.Itemtitle)
ItemCategory.setText(itempost.ItemCategory)
ItemPrice.setText(itempost.ItemPrice)
val requestOptions = RequestOptions()
.placeholder(R.drawable.ic_baseline_brush_24)
.error(R.drawable.ic_baseline_brush_24)
Glide.with(itemView.getContext())
.applyDefaultRequestOptions(requestOptions)
.load(itempost.image)
.into(ItemImage)
}
}
}
This is where I use the SubmitItem() in my ItemRecyclerAdapter.kt which contains items of my shop app and displayed from another fragment. CartAdapter is the adapter of the cart that I recently created in order to access the function SubmitItem().
ItemButton.setOnClickListener()
{
Toast.makeText(itemView.context, "${itempost.image}, ${itempost.Itemtitle}, ${itempost.ItemPrice}", Toast.LENGTH_SHORT).show()
CartAdapter.SubmitItem(ItemPost(itempost.image,itempost.Itemtitle,itempost.ItemCategory,itempost.ItemPrice))
}
This is my code for my fragments
ShopFragment.kt contains the recyclerview of my items.
package com.example.karawapplication.fragments
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import com.android.volley.Request
import com.android.volley.VolleyError
import com.android.volley.toolbox.JsonArrayRequest
import com.android.volley.toolbox.Volley
import com.example.karawapplication.ItemRecyclerAdapter
import com.example.karawapplication.R
import com.example.karawapplication.models.ItemPost
import kotlinx.android.synthetic.main.fragment_shop.*
import org.json.JSONException
import org.json.JSONObject
// 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 [Shop.newInstance] factory method to
* create an instance of this fragment.
*/
class Shop : Fragment(){
// TODO: Rename and change types of parameters
private lateinit var ItemAdapter : ItemRecyclerAdapter
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
return inflater.inflate(R.layout.fragment_shop, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initRecyclerView()
addDataSet()
}
// CUSTOM FUNCTIONS
private fun addDataSet()
{
createDataset {
list -> ItemAdapter.SubmitList(list)
}
}
private fun initRecyclerView()
{
ShopRecycleView.layoutManager = LinearLayoutManager(activity)
ItemAdapter = ItemRecyclerAdapter()
ShopRecycleView.adapter = ItemAdapter
//Toast.makeText(context, "Recycler Trigger", Toast.LENGTH_SHORT).show()
}
// https://tutorials.eu/json-parsing-and-how-to-use-gson-in-android/
// Generates the data for the recycleview
fun createDataset(onSuccess: (List<ItemPost>) -> Unit){
val url = "http://api.karawcraftventure.com/item"
// LIST DATA VARIABLE FOR RECYCLEVIEW
val list = ArrayList<ItemPost>()
// VOLLEY API REQUEST
val Queue = Volley.newRequestQueue(activity)
val jsonObject = JsonArrayRequest(
Request.Method.GET,url,null,
{response ->
try
{
for (i in 0 until response.length())
{
val item : JSONObject = response.getJSONObject(i)
val API_Image : String = item.getString("product_url_image")
val API_ItemName : String = item.getString("product_name")
val API_Price : String = item.getString("product_price")
val API_Category : String = item.getString("product_category")
// Toast Notif if data is extracted or not
//Toast.makeText(context, "$API_ItemName - $API_Price - $API_Category", Toast.LENGTH_SHORT).show()
list.add(ItemPost(API_Image, API_ItemName, API_Category, API_Price))
}
onSuccess(list)
}
catch (e: JSONException)
{
e.printStackTrace()
}
},
{ error: VolleyError? -> Toast.makeText(context, error?.message.toString(), Toast.LENGTH_SHORT).show()
}
)
Queue.add(jsonObject)
}
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 Shop.
*/
// TODO: Rename and change types and number of parameters
#JvmStatic
fun newInstance(param1: String, param2: String) =
Shop().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
Cart.kt fragment on the other hand, contains my shopping cart.
package com.example.karawapplication.fragments
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 com.example.karawapplication.CartRecyclerAdapter
import com.example.karawapplication.ItemRecyclerAdapter
import com.example.karawapplication.R
import kotlinx.android.synthetic.main.fragment_cart.*
import kotlinx.android.synthetic.main.fragment_shop.*
import kotlinx.android.synthetic.main.fragment_shop.ShopRecycleView
// 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 [Cart.newInstance] factory method to
* create an instance of this fragment.
*/
class Cart : Fragment() {
// TODO: Rename and change types of parameters
private lateinit var ItemAdapter : CartRecyclerAdapter
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 onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initRecyclerView()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_cart, container, false)
}
private fun initRecyclerView()
{
Cart_RecyclerView.layoutManager = LinearLayoutManager(activity)
ItemAdapter = CartRecyclerAdapter()
Cart_RecyclerView.adapter = ItemAdapter
//Toast.makeText(context, "Recycler Trigger", Toast.LENGTH_SHORT).show()
}
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 Cart.
*/
// TODO: Rename and change types and number of parameters
#JvmStatic
fun newInstance(param1: String, param2: String) =
Cart().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
The code has no errors, but it does not show any output in my cart recyclerview fragment.
(I'm going to follow the "starts with a lowercase letter" convention for variables and functions here, because I'm tired and it's less confusing)
Your submitItem function just creates a new copy of items (items.toMutableList()) and adds to that. Then it's immediately discarded when you exit the function.
All your adapter code that handles your data (getItemCount, onBindViewHolder) references items, so you need to update that instead, and let the adapter know it's changed:
fun submitItem(item: ItemPost) {
// plus creates a copy, but adds the new item without making the result mutable
items = items.plus(item)
// we also need to tell the adapter that the data has changed, so it can
// update - there are more efficient calls (you'll probably get a lint
// warning telling you to use one) but this is the simplest
notifyDataSetChanged()
}
This is just making your adapter update correctly - I'm assuming you've got your communication between the two fragments set up properly (communicating through the parent activity, passing one fragment's adapter into the other, using a view model). Have a read of this if you need to: Communicating with fragments
My Main Activity Class
This is implemented to learn recycler view and to handle clicks. The below code works fine but while implementing listener I got confused. All the doubts are listed below. Do help.
package com.suasnom.pizzarecyclerview
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity(), isClickedInterface {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//implementing recycler view
recycler_view.layoutManager= LinearLayoutManager(this)
val data = fetchData()
val adapter = CustomAdapter(data, this)
recycler_view.adapter = adapter
}
fun fetchData(): ArrayList<String> {
val list_Strings = ArrayList<String>()
var str = ""
for(i in 1..100){
str = "${i} line"
list_Strings.add(str)
}
return list_Strings
}
override fun onItemClicked(item: String) {
Toast.makeText(this, "$item", Toast.LENGTH_LONG).show()
}
}
In this statement I passed
val adapter = CustomAdapter(data, this)
and it allows me to override the below method:
override fun onItemClicked(item: String) {
Toast.makeText(this, "$item", Toast.LENGTH_LONG).show()
}
The below code is for recycler view adapter where I write that interface:
package com.suasnom.pizzarecyclerview
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class CustomAdapter(val list_strings: ArrayList<String>, private val listner: isClickedInterface): RecyclerView.Adapter<PizzaViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PizzaViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.row, parent, false)
val pizzaObject = PizzaViewHolder(view)
view.setOnClickListener {
listner.onItemClicked(list_strings[pizzaObject.adapterPosition])
}
return pizzaObject
}
override fun onBindViewHolder(holder: PizzaViewHolder, position: Int) {
val data_incoming = list_strings[position]
holder.text_message.text = data_incoming
}
override fun getItemCount(): Int {
return list_strings.size
}
}
class PizzaViewHolder(private val view: View): RecyclerView.ViewHolder(view){
val text_message = view.findViewById<TextView>(R.id.textrow)
}
interface isClickedInterface{
fun onItemClicked(item: String){}
}
Any idea how this is working. Please Help ...
inside CustomAdapter on the bottom you have declared isClickedInterface (it might be declared anywhere else or as separated file). it is implemented by your MainActivity (after :), so you have to set this interface methods inside implementing class - so in Activity appears onItemClicked(item: String) method
now your CustomAdapter have constructor param to pass this interface (second one). for initiating new instance of adapter you have pass implemented interface, in here you may pass whole Activity as it implements desired interface (val adapter = CustomAdapter(data, this) - this points on Activity, which is also an isClickedInterface interface instance)
now inside onCreateViewHolder you are setting setOnClickListener and inside of it you are calling method from passed interface in constructor
I'm trying to set up recycler view to work with the fragment I created, and in MainActivity after "val recyclerV = findViewById(R.id.plantsRecyclerView)" I'm getting " java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.myapplication/com.example.myapplication.MainActivity}: java.lang.NullPointerException: findViewById<RecyclerVie…(R.id.plantsRecyclerView) must not be null" even though the recycler with that Id exists, any ideas how can I fix that?
Thanks in advance!
(code:
Main Activity:
private var numPlants = 4
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//there is some parts that I'm not posting because they are not relevant (at least I think so <it's mainly code that creates notifications>)
val recyclerV = findViewById<RecyclerView>(R.id.plantsRecyclerView)
recyclerV.layoutManager = LinearLayoutManager(this)
recyclerV.adapter = MyRecyclerViewAdapter(this, numPlants)
}
}
MyRecyclerViewAdapter:
package com.example.myapplication
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
class MyRecyclerViewAdapter(private val context: Context, private val numPlants: Int) :
RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.card_layout, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(position)
}
override fun getItemCount() = numPlants
inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
fun bind(position: Int){
// not implemented yet
}
}
}
fragment main (the fragment that recycler view inside of 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"
android:background="#color/darker_green"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/plantsRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainFragment (I haven't changed anything here after creation, and I suppose that is the problem):
package com.example.myapplication
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
// 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 [MainFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class MainFragment : 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
return inflater.inflate(R.layout.fragment_main, 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 MainFragment.
*/
// TODO: Rename and change types and number of parameters
#JvmStatic
fun newInstance(param1: String, param2: String) =
MainFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
Because your RecyclerView is in MainFragment. Not in MainActivity. Make your RecyclerView in MainFragment.
Currently, I am trying to implement RecyclerView inside of Fragment but I cannot find a way to display it.
Here is my MainActivity:
package com.example
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import com.example.projectdrivemark.R
import com.example.projectdrivemark.databinding.ActivityMainBinding
import com.example.recyclerView.MockDatabase.Companion.createMockData
import com.example.recyclerView.PostAdapter
import com.example.recyclerView.RecyclerViewFragment
import com.example.tempConverter.TempConverterFragment
import com.example.uploaderView.UploaderFragment
class MainActivity : AppCompatActivity(), PostAdapter.OnPostClickListener {
private lateinit var binding: ActivityMainBinding
val dummyList = createMockData()
val adapter = PostAdapter(dummyList, this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
title = "First Kotlin App"
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val recyclerView = RecyclerViewFragment()
val tempConverterView = TempConverterFragment()
// recyclerView.layoutManager = LinearLayoutManager(this)
val uploaderView = UploaderFragment(this)
setFragmentView(recyclerView)
binding.bottomNavBar.setOnNavigationItemSelectedListener {
when(it.itemId){
R.id.listView -> setFragmentView(recyclerView)
R.id.tempConverterView -> setFragmentView(tempConverterView)
R.id.videoUploaderView -> setFragmentView(uploaderView)
}
true
}
}
private fun setFragmentView(fragment: Fragment){
supportFragmentManager.beginTransaction().apply {
replace(R.id.main_fragment_view, fragment)
//Will return to previous page when tap "Back Button" on the phone
addToBackStack(null)
commit()
}
}
override fun onEditPost(position: Int){
val clickedPost = dummyList[position]
clickedPost.title = "Updated title"
clickedPost.body = "Updated body"
adapter.notifyItemChanged(position)
}
override fun onDeletePost(position: Int) {
dummyList.removeAt(position)
adapter.notifyItemRemoved(position)
}
fun celsiusFunction(view: View) {
val tempConverter = TempConverterFragment()
tempConverter.celsiusFunction(view)
}
fun farenheitFunction(view: View){
val fahrenheitConverter = TempConverterFragment()
fahrenheitConverter.farenheitFunction(view)
}
}
Here is my RecyclerFragment:
package com.example.recyclerView
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.MainActivity
import com.example.projectdrivemark.R
class RecyclerViewFragment: Fragment() {
var adapter: RecyclerView.Adapter<PostAdapter.PostViewHolder>? = null
var layoutManager: RecyclerView.LayoutManager? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreate(savedInstanceState)
val binding = inflater.inflate(R.layout.fragment_list, container, false)
return binding
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.apply{
layoutManager = LinearLayoutManager(activity)
adapter = PostAdapter(dummyData = ArrayList<Post>(), MainActivity())
}
}
}
Here is my PostAdapter code:
package com.example.recyclerView
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.MainActivity
import com.example.projectdrivemark.R
class PostAdapter(val dummyData: ArrayList<Post>, val myListener: MainActivity) : RecyclerView.Adapter<PostAdapter.PostViewHolder>() {
inner class PostViewHolder(postView: View) : RecyclerView.ViewHolder(postView), View.OnClickListener{
val iconImage: ImageView = postView.findViewById(R.id.icon_image_view)
val title: TextView = postView.findViewById(R.id.title)
val body: TextView = postView.findViewById(R.id.body)
val deleteIcon: ImageView = postView.findViewById(R.id.delete_post_image)
val editIcon: ImageView = postView.findViewById(R.id.edit_post_image)
init {
deleteIcon.setOnClickListener(this)
editIcon.setOnClickListener(this)
}
override fun onClick(v: View?){
val position = adapterPosition
if(v?.id == editIcon.id){
myListener.onEditPost(position)
}else{
myListener.onDeletePost(position)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostViewHolder {
val postView = LayoutInflater.from(parent.context).inflate(R.layout.post, parent, false)
return PostViewHolder(postView)
}
override fun onBindViewHolder(holder: PostViewHolder, position: Int) {
val currentPost = dummyData[position]
holder.iconImage.setImageResource(currentPost.image)
holder.title.text = currentPost.title
holder.body.text = currentPost.body
}
override fun getItemCount(): Int {
return dummyData.size
}
interface OnPostClickListener{
fun onEditPost(position: Int)
fun onDeletePost(position: Int)
}
}
If anyone saw something I miss, please do tell me because I am stuck at how to display the RecyclerView. Any help would be appreciate.
Edit:
Here is my MockDatabase:
package com.example.recyclerView
import com.example.projectdrivemark.R
class MockDatabase {
companion object{
fun createMockData(): ArrayList<Post>{
val list = ArrayList<Post>()
for(i in 0 until 20){
val imageToSelect = when (i % 3){
0 -> R.drawable.ic_baseline_account_balance
1 -> R.drawable.ic_baseline_account_circle
2 ->R.drawable.ic_baseline_ac_unit
else -> R.drawable.ic_baseline_access_alarms
}
list.add(
Post(
imageToSelect,
title = "Title post of $i",
body = "Title post of $i"
)
)
}
return list
}
}
}
Here is my Layout_list xml file:
<?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">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycleViewMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
tools:listitem="#layout/recycler_view_item" />
</androidx.constraintlayout.widget.ConstraintLayout>
You need to cast view to RecyclerView to access RecyclerView properties inside apply, also you cannot create instances of Activity by yourself,
MainActivity() is wrong and won't work, instead use requireActivity() which will provide context of Activity to which Fragment is attached to
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewbyId<RecyclerView>(R.id.recycleViewMain).apply{
layoutManager = LinearLayoutManager(requireActivity())
adapter = PostAdapter(dummyData = ArrayList<Post>(), requireActivity() as OnPostClickListener)
}
}
Also, as #Necronet mentioned, add some mock data to see if it is actually rendering.
UPDATE
Instead of passing MainActivity to Adapter, pass OnPostClickListener
adapter = PostAdapter(dummyData = ArrayList<Post>(), requireActivity() as OnPostClickListener)
Adapter
class PostAdapter(val dummyData: ArrayList<Post>, val myListener: OnPostClickListener) : RecyclerView.Adapter<PostAdapter.PostViewHolder>(){
// you can use myListener to call methods
}
It's simple you are passing empty dummyData try using createMockData() instead. Also as a rule of thumb in Android, never ever instatiate an Activity yourself, if you need the reference from within a fragment you can use getActivity() method. So this:
adapter = PostAdapter(dummyData = ArrayList<Post>(), MainActivity())
Should be:
adapter = PostAdapter(dummyData = createMockData(), getActivity() as MainActivity)