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
Related
I've been trying to pass data(the email and phone of a user) from my adapter to my fragment. From what I've read online I should use a interface for this but I cant the data into my fragment still. Can anyone explain in steps how I should add a interface and how to put data into my interface from my adapter so I can call it in my fragment. Or is there another way to pass data from my adapter to my fragment. Below are my adapter and my fragment.
Adapter:
package ie.wit.savvytutor.adapters
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.recyclerview.widget.RecyclerView
import ie.wit.savvytutor.R
import ie.wit.savvytutor.activity.MainActivity
import ie.wit.savvytutor.fragments.ViewChatFragment
import ie.wit.savvytutor.models.UserModel
class UserAdapter(private val userList: ArrayList<UserModel>, val context: Context) :
RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val itemView =
LayoutInflater.from(parent.context).inflate(R.layout.user_layout, parent, false)
return UserViewHolder(itemView)
}
class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val username: TextView = itemView.findViewById(R.id.userNameView)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int, ) {
val currentItem = userList[position]
holder.username.text = currentItem.email
holder.itemView.setOnClickListener {
println(currentItem)
val optionsFrag = ViewChatFragment()
(context as MainActivity).getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, optionsFrag, "OptionsFragment").addToBackStack(
null
)
.commit()
}
}
override fun getItemCount(): Int {
return userList.size
}
}
Fragment
package ie.wit.savvytutor.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.Nullable
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.*
import ie.wit.savvytutor.R
import ie.wit.savvytutor.adapters.UserAdapter
import ie.wit.savvytutor.models.UserModel
class TutorChatFragment : Fragment() {
private lateinit var userRecyclerView: RecyclerView
private lateinit var userArrayList: ArrayList<UserModel>
private lateinit var dbRef: DatabaseReference
private lateinit var mAuth: FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dbRef = FirebaseDatabase.getInstance("DATABASE LINK").getReference("Users").ref
mAuth = FirebaseAuth.getInstance()
}
#Nullable
override fun onCreateView(
inflater: LayoutInflater,
#Nullable container: ViewGroup?,
#Nullable savedInstanceState: Bundle?
): View {
//inflate the fragment layout
val root = inflater.inflate(R.layout.tutor_chat_fragment, container, false)
userRecyclerView = root.findViewById(R.id.userListView)
userRecyclerView.layoutManager = LinearLayoutManager(context)
userRecyclerView.setHasFixedSize(true)
userArrayList = arrayListOf<UserModel>()
getUser()
return root
}
private fun getUser() {
userArrayList.clear()
dbRef.addValueEventListener(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
for (postSnapshot in snapshot.children) {
val currentUser = postSnapshot.getValue(UserModel::class.java)
//BUG FIX 1.26.13
val email = currentUser?.email
if (email != null) {
userArrayList.add(currentUser)
}
userRecyclerView.adapter?.notifyDataSetChanged()
userRecyclerView.adapter = context?.let { UserAdapter(userArrayList, it) }
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
}
If you want to use an interface, you just need to define one with a function to receive your data, make the fragment implement it, then pass the fragment to the adapter as an implementation of that interface:
data class UserData(val email: String, val phone: String)
class UserAdapter(
private val userList: ArrayList<UserModel>,
val context: Context,
val handler: UserAdapter.Callbacks // added this here, so you're passing it in at construction
) : RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
...
private fun doWhatever(email: String, phone: String) {
// pass the data to the handler (which will probably be your Fragment)
handler.handleUserData(UserData(email, phone))
}
// nested inside the UserAdapter class to keep things tidy
interface Callbacks {
fun handleUserData(data: UserData)
}
}
Then in the Fragment:
// add the Callbacks interface type
class TutorChatFragment : Fragment(), UserAdapter.Callbacks {
override fun onCreateView(
inflater: LayoutInflater,
#Nullable container: ViewGroup?,
#Nullable savedInstanceState: Bundle?
): View {
...
userRecyclerView.layoutManager = LinearLayoutManager(context)
// set up the adapter here, passing this fragment as the Callbacks handler
userRecyclerView.adapter = UserAdapter(userArrayList, context, this)
...
}
// interface implementation
override fun handleUserData(data: UserData) {
// whatever
}
}
And that's it. You're not hardcoding a dependency on that particular Fragment type, just the interface, and this fragment implements it so it can pass itself.
A more Kotliny way to do it is to ignore interfaces and just pass a function instead
class UserAdapter(
private val userList: ArrayList<UserModel>,
val context: Context,
val handler: (UserData) -> Unit // passing a function that takes a UserData instead
) : RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
...
private fun doWhatever(email: String, phone: String) {
// call the handler function with your data (you can write handler.invoke() if you prefer)
handler(UserData(email, phone))
}
}
// no interface this time
class TutorChatFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
#Nullable container: ViewGroup?,
#Nullable savedInstanceState: Bundle?
): View {
...
userRecyclerView.layoutManager = LinearLayoutManager(context)
// pass in a handler function
userRecyclerView.adapter = UserAdapter(userArrayList, context) { userData ->
handleUserData(userData)
}
// or if you're just passing it to that function down there,
// you could do UserAdapter(userArrayList, context, ::handleUserData)
// and pass the function reference
...
}
// might be convenient to still do this in its own function
private fun handleUserData(data: UserData) {
// whatever
}
}
Ideally you should be doing what I've done there - create the adapter once during setup, and have a function on it that allows you to update it. Your code creates a new one each time you get data. You do this the same way in both though
Your other option is using a view model that the adapter and fragment both have access to, but this is how you do the interface/callback approach
Actually there is one very easy way to get data from your adapter in to your fragment or activity. It is called using Higher Order Functions.
In your adapter
Add higher order function in your adapter.
class UserAdapter(private val userList: ArrayList<UserModel>, val context: Context) :
RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
//your rest of the adapter's code
private var onItemClickListener:((UserModel)->Unit)? = null
fun setOnItemClickListener(listener: (UserModel)->Unit) {
onItemClickListener = listener
}
}
In Your UserViewHolder
val rootView = itemView.rootView
In Your onBindViewHolder
set a click listener on rootView
holder.rootView.setOnClickListener {
onItemClickListener?.let{
it(currentItem)
}
}
In Your Fragment
//create the instance of UserAdapter
userAdapter.setOnItemClickListener {
//here you have your UserModel in your fragment, do whatever you want to with it
}
And, a suggestion in the last. Start using ViewBinding, it will save you from a lots of hectic work.
I got this error while using the replace function to change the frame of the main Activity with the fragment that I created it shows type mismatch required fragment found DashboardFragmentComapnion
package com.shivanshu.bookhub
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.MenuItem
import android.widget.FrameLayout
import android.widget.Toast
import android.widget.Toolbar
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.material.navigation.NavigationView
class MainActivity : AppCompatActivity() {
lateinit var drawerLayout: DrawerLayout
lateinit var coordinatorLayout: CoordinatorLayout
lateinit var toolbar: androidx.appcompat.widget.Toolbar
lateinit var frameLayout: FrameLayout
lateinit var navigationView: NavigationView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
drawerLayout = findViewById(R.id.drawerLayout)
coordinatorLayout = findViewById(R.id.coordinatorLayout)
toolbar = findViewById(R.id.toolbar)
frameLayout = findViewById(R.id.frameLayout)
navigationView = findViewById(R.id.navigationView)
setUpToolbar()
val actionBarDrawerToggle = ActionBarDrawerToggle(
this#MainActivity,
drawerLayout,
R.string.open_drawer,
R.string.close_drawer
)
drawerLayout.addDrawerListener(actionBarDrawerToggle)
actionBarDrawerToggle.syncState()
navigationView.setNavigationItemSelectedListener {
when(it.itemId){
R.id.dashboard -> {
supportFragmentManager.beginTransaction().
replace(R.id.frameLayout, DashboardFragment).commit()
}
R.id.profile ->{
Toast.makeText(this#MainActivity,"Profile Clicked",Toast.LENGTH_LONG)
}
R.id.about_app -> {
Toast.makeText(this#MainActivity,"About Section Clicked",Toast.LENGTH_LONG)
}
R.id.favourites -> {
Toast.makeText(this#MainActivity,"Favourites Clicked",Toast.LENGTH_LONG)
}
}
return#setNavigationItemSelectedListener true
}
}
fun setUpToolbar() {
setSupportActionBar(toolbar)
supportActionBar?.title = "Toolbar Title"
supportActionBar?.setHomeButtonEnabled(true)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
if(id == android.R.id.home){
drawerLayout.openDrawer(GravityCompat.START )
}
return super.onOptionsItemSelected(item)
}
}
The fragment file:
package com.shivanshu.bookhub
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 [DashboardFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class DashboardFragment : 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_dashboard2, 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 DashboardFragment.
*/
// TODO: Rename and change types and number of parameters
#JvmStatic fun newInstance(param1: String, param2: String) =
DashboardFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
I implement the NavigationDrawer having various menu items when I click on the dashboard menu item it will open the dashboard fragment I created
You should pass an instance of DashboardFragment to replace function.
val dashboardFragmentInctance = DashboardFragment.newInstance("param","param")
supportFragmentManager.beginTransaction().
replace(R.id.frameLayout, dashboardFragmentInctance).commit()
Here is the piece of code that I have been trying.
The adapter code is below.
Made an array list for storing the data, and want to display it in the fragment using grid view. The app is running but its not showing the grid layout.
import android.os.strictmode.WebViewMethodCalledOnWrongThreadViolation
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ImageView
import com.shyptsolution.nitrr.R
class GalleryAdapter(gallerylist:ArrayList<GalleryDataModel>):BaseAdapter() {
var galleryList=gallerylist
var image:ImageView?=null
override fun getCount(): Int {
return galleryList.size
}
override fun getItem(position: Int): Any {
return galleryList[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
var inflator=LayoutInflater.from(parent?.context).inflate(R.layout.gallery_ticket,null)
image=inflator.findViewById(R.id.gallery)
image?.setImageResource(galleryList[position].image)
return inflator
}
}
The code for the fragment is given below
package com.shyptsolution.nitrr.gallery
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.GridView
import com.shyptsolution.nitrr.R
// 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 [Gallery.newInstance] factory method to
* create an instance of this fragment.
*/
class Gallery : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
lateinit var adapter:GalleryAdapter
var gallerygrid=activity?.findViewById<GridView>(R.id.galllerygrid)
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
adapter= GalleryAdapter(GalleryData.galleryphoto)
gallerygrid?.adapter=adapter
return inflater.inflate(R.layout.fragment_gallery, 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 Gallery.
*/
// TODO: Rename and change types and number of parameters
#JvmStatic
fun newInstance(param1: String, param2: String) =
Gallery().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
How can I do it, I have done recycler view in the fragment but couldn't figure out the problem here.
Here is the list of changes that I have made and now its working fine.
package com.shyptsolution.nitrr.gallery
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.GridLayout
import android.widget.GridView
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.GridLayoutManager
import com.shyptsolution.nitrr.R
// 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 [Gallery.newInstance] factory method to
* create an instance of this fragment.
*/
class Gallery : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
var listofphoto=GalleryData.galleryphoto
lateinit var adapter:GalleryAdapter
var gallerygrid=activity?.findViewById<GridView>(R.id.galllerygrid)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
// adapter=GalleryAdapter(listofphoto)
// var galleryview=activity?.findViewById<GridView>(R.id.galllerygrid)
// galleryview?.adapter=adapter
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
// adapter= GalleryAdapter(GalleryData.galleryphoto)
// gallerygrid?.adapter=adapter
var view:View=inflater.inflate(R.layout.fragment_gallery, container, false)
gallerygrid=view.findViewById(R.id.galllerygrid)
adapter= GalleryAdapter(listofphoto)
gallerygrid?.adapter=adapter
return view
}
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 Gallery.
*/
// TODO: Rename and change types and number of parameters
#JvmStatic
fun newInstance(param1: String, param2: String) =
Gallery().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
Added the Gallery adapter and then set the grid adapter. Its working fine.
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.
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)
}
}