How i can use RecyclerView inside a fragment - android

i try to implement recyclerView inside a fragment. When i try to open my app it crashes. i have a BottomNavigationMenu and i want to implement recyclerView inside one of the fragments. here is my code:
HomeFragment.kt
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
import kotlinx.android.synthetic.main.fragment_home.*
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
private var layoutManager: RecyclerView.LayoutManager? = null
private var adapter:
RecyclerView.Adapter<RecyclerAdapter.ViewHolder>? = null
class HomeFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
}
layoutManager = LinearLayoutManager(context)
recycler.layoutManager = layoutManager
adapter = RecyclerAdapter()
recycler.adapter = adapter
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
companion object {
#JvmStatic
fun newInstance(param1: String, param2: String) =
HomeFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
how i can implement it correctly

Move your calls to your RecyclerView from onCreate to onViewCreated
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
layoutManager = LinearLayoutManager(context)
recycler.layoutManager = layoutManager
adapter = RecyclerAdapter()
recycler.adapter = adapter
}

Related

Having the same error but unable to solve it

I'm pretty new at android development, and I'm trying to fetch data from Realtime Database into recycler view in a fragment. I've seen pretty much every tutorial how to do it but it simply wont work. Below I'll put the source code.
main fragment (where i need to fetch the data)
package hr.ferit.tomislav.lucic5.tl5_projekt
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.firebase.database.*
private lateinit var transactions : ArrayList<Transaction>
private lateinit var recyclerView : RecyclerView
private lateinit var adapter: TransactionAdapter
private lateinit var loadingData : TextView
private lateinit var dbRef : DatabaseReference
class MainFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_main, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val newTransaction = view.findViewById<FloatingActionButton>(R.id.addBtn)
recyclerView = view.findViewById(R.id.recyclerview)
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.setHasFixedSize(true)
loadingData = view.findViewById(R.id.tvLoadingData)
transactions = arrayListOf<Transaction>()
getTransactionData()
newTransaction?.setOnClickListener {
val homeFragment = AddTransactionFragment()
val transaction = fragmentManager?.beginTransaction()
transaction?.replace(R.id.mainFrame,homeFragment)
transaction?.addToBackStack(null)
transaction?.commit()
}
}
private fun updateDashboard(){
var balance = view?.findViewById<TextView>(R.id.balance)
var expense = view?.findViewById<TextView>(R.id.expense)
var budget = view?.findViewById<TextView>(R.id.budget)
val totalAmount = transactions.map { it.amount.toInt() }.sum()
val budgetAmount = transactions.filter { it.amount.toInt() >0 }.map{it.amount.toInt()}.sum()
val expenseAmount = totalAmount - budgetAmount
balance?.text = "$ %.2f".format(totalAmount)
budget?.text = "$ %.2f".format(budgetAmount)
expense?.text = "$ %.2f".format(expenseAmount)
}
private fun getTransactionData(){
recyclerView.visibility= View.GONE
loadingData.visibility = View.VISIBLE
dbRef = FirebaseDatabase.getInstance().getReference("transactions")
dbRef.addValueEventListener(object: ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
transactions.clear()
if (snapshot.exists()) {
for (tranSnap in snapshot.children) {
val tranData =
tranSnap.getValue(Transaction::class.java)
transactions.add(tranData!!)
}
val mAdapter = TransactionAdapter(transactions)
recyclerView.adapter = mAdapter
recyclerView.visibility = View.VISIBLE
loadingData.visibility = View.GONE
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
}
adapter
package hr.ferit.tomislav.lucic5.tl5_projekt
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class TransactionAdapter(private val trList:ArrayList<Transaction>) :
RecyclerView.Adapter<TransactionAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.transaction_layout,parent,false)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val curTransaction = trList[position]
holder.amount.text = curTransaction.amount
holder.label.text = curTransaction.label
}
override fun getItemCount(): Int {
return trList.size
}
fun addItem(transaction: Transaction){
trList.add(0, transaction)
notifyItemInserted(0)
}
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val label : TextView = itemView.findViewById(R.id.label)
val amount : TextView = itemView.findViewById(R.id.amount)
}
}
data class for Transaction
package hr.ferit.tomislav.lucic5.tl5_projekt
data class Transaction (
var label: String,
var amount: String,
var description: String
)
I don't know what can I do else.. it's always the same error Class hr.ferit.tomislav.lucic5.tl5_projekt.Transaction does not define a no-argument constructor. If you are using ProGuard, make sure these constructors are not stripped. That happens after i try to add a new transaction:
package hr.ferit.tomislav.lucic5.tl5_projekt
import android.annotation.SuppressLint
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageButton
import androidx.core.widget.addTextChangedListener
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ktx.database
import com.google.firebase.ktx.Firebase
class AddTransactionFragment : Fragment() {
private lateinit var database : FirebaseDatabase
#SuppressLint("MissingInflatedId")
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_add_transaction2, container, false)
val addBtn = view.findViewById<Button>(R.id.addTransactionBtn)
val titleInput = view.findViewById<TextInputEditText>(R.id.titleInput)
val amountInput = view.findViewById<TextInputEditText>(R.id.amountInput)
val descriptionInput = view.findViewById<TextInputEditText>(R.id.descriptionInput)
val titleLayout = view.findViewById<TextInputLayout>(R.id.titleLayout)
val amountLayout = view.findViewById<TextInputLayout>(R.id.amountLayout)
val closeBtn = view.findViewById<ImageButton>(R.id.closeBtn)
var recyclerAdapter : TransactionAdapter
var database = Firebase.database.reference
val userId = FirebaseAuth.getInstance().currentUser!!.uid
titleInput.addTextChangedListener {
if(it!!.count() > 0)
titleLayout.error = null
}
amountInput.addTextChangedListener {
if(it!!.count() > 0)
amountLayout.error = null
}
addBtn.setOnClickListener{
val title = titleInput.text.toString()
val description = descriptionInput.text.toString()
val amount = amountInput.text.toString()
var transactionToAdd = Transaction(
label = titleInput.text.toString(),
description = descriptionInput.text.toString(),
amount = amountInput.text.toString()
)
val list = ArrayList<Transaction>()
recyclerAdapter = TransactionAdapter(list)
if(title.isEmpty()) {
titleLayout.error = "Please enter a valid title"
}
if(amount.isEmpty()) {
amountLayout.error = "Please enter a valid amount"
}
else{
database.child("transactions").push().setValue(transactionToAdd)
.addOnSuccessListener {
recyclerAdapter.addItem(transactionToAdd)
var homeFragment = MainFragment()
val transaction = fragmentManager?.beginTransaction()
transaction?.replace(R.id.mainFrame,homeFragment)
transaction?.commit()
}
}
}
closeBtn.setOnClickListener{
var homeFragment = MainFragment()
val transaction = fragmentManager?.beginTransaction()
transaction?.replace(R.id.mainFrame,homeFragment)
transaction?.commit()
}
return view
}
}
I tried everything, basically every tutorial I found on youtube but nothing helped. I don't know what's the problem, I get that it is something to do with the data class and the constructor but im unable to solve it.

Problem with Kotlin Firebase RecyclerView

Im trying to make a user recyclerview in a fragment, but i can't make it to work.
The idea is to get the users from the database (except for the actual user loged in) and add the user data to the list. The problem is that the list doesn't even apear.
I don't know much about Kotlin, i've recently started and Im struggling with this. Also I don't know where the problem is.
If you need the XML, ask for it
My Fragment:
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
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.auth.FirebaseUser
import com.google.firebase.database.*
#Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
class UsersFragment:Fragment(R.layout.fragment_users) {
private lateinit var recyclerView: RecyclerView
private lateinit var adapterUsers: AdapterUsers
private lateinit var userList: MutableList<ModelUsers>
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
super.onCreate(savedInstanceState)
(activity as AppCompatActivity).supportActionBar?.title = "Users"
val view: View = inflater.inflate(R.layout.fragment_users, container, false)
userList = ArrayList()
getAllUsers()
adapterUsers = AdapterUsers(context!!, userList)
recyclerView = view.findViewById(R.id.users_recyclerView)
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
recyclerView.adapter = adapterUsers
return view
}
private fun getAllUsers() {
val fUser: FirebaseUser = FirebaseAuth.getInstance().currentUser
val ref: DatabaseReference = FirebaseDatabase.getInstance().getReference("Users")
ref.addValueEventListener(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
Log.d("FUNCTION", "ENTER")
userList.clear()
for(ds: DataSnapshot in snapshot.children){
val modelUsers: ModelUsers? = ds.getValue(ModelUsers::class.java)
if(!modelUsers?.uid.equals(fUser.uid)){
userList.add(modelUsers!!)
Toast.makeText(activity, "IN", Toast.LENGTH_SHORT).show()
}
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
companion object {
fun newInstance(): ProfileFragment {
return ProfileFragment()
}}}
My Custom Adapter:
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import java.lang.Exception
open class AdapterUsers(private var context:Context, private var userList: MutableList<ModelUsers> = mutableListOf<ModelUsers>()): RecyclerView.Adapter<AdapterUsers.UserViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.row_users, parent, false)
return UserViewHolder(view)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val userImage = userList[position].image
val userName = userList[position].name
val userEmail = userList[position].email
holder.mNameTv.text = userName
holder.mEmailTv.text = userEmail
try {
Picasso.get().load(userImage).placeholder(R.drawable.ic_default_image_color).into(holder.mAvatarIv)
}
catch (e: Exception){
}
holder.itemView.setOnClickListener{
Toast.makeText(context, userEmail, Toast.LENGTH_SHORT).show()
}
}
override fun getItemCount(): Int {
return userList.size
}
inner class UserViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val mAvatarIv: ImageView = itemView.findViewById(R.id.avatarIv)
val mNameTv: TextView = itemView.findViewById(R.id.nameTv)
val mEmailTv: TextView = itemView.findViewById(R.id.emailTv)
}
}
Model for Users:
data class ModelUsers(
var name: String? = null,
var email: String? = null,
var search: String? = null,
var phone: String? = null,
var image: String? = null,
var cover: String? = null,
var uid: String? = null)
You should refresh your recycler view adapter after your data have changed, try to change your code like this:
class UsersFragment:Fragment(R.layout.fragment_users) {
private lateinit var recyclerView: RecyclerView
private var userList = mutableListOf<ModelUsers>()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
super.onCreate(savedInstanceState)
(activity as AppCompatActivity).supportActionBar?.title = "Users"
val view: View = inflater.inflate(R.layout.fragment_users, container, false)
recyclerView = view.findViewById<RecyclerView>(R.id.users_recyclerView).apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(requireContext())
adapter = AdapterUsers(requireContext(), mutableListOf())
}
getAllUsers()
return view
}
private fun getAllUsers() {
val fUser: FirebaseUser = FirebaseAuth.getInstance().currentUser
val ref: DatabaseReference = FirebaseDatabase.getInstance().getReference("Users")
ref.addValueEventListener(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
Log.d("FUNCTION", "ENTER")
userList.clear()
for(ds: DataSnapshot in snapshot.children) {
val modelUsers: ModelUsers? = ds.getValue(ModelUsers::class.java)
if(!modelUsers?.uid.equals(fUser.uid)){
userList.add(modelUsers!!)
Toast.makeText(activity, "IN", Toast.LENGTH_SHORT).show()
}
recyclerView.adapter = AdapterUsers(requireContext(), userList)
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
companion object {
fun newInstance(): ProfileFragment {
return ProfileFragment()
}
}
}
The problem is that when you use your adapter the list is empty, then maybe 0.2 seconds later, you have fetched the users from the database and populated the list, but the adapter doesn't know the list has changed. After you have populated the list you can call notifyDataSetChanged() on your Adapter to let it know the list has changed.

Slow fragment accessing using SharedFragment with `by activityViewModels`?

So, I was told by someone to use shared view model previously in my another question (How do I store a data from a fragment so it can be reuse in a MVVM architecture?)
The code below is not using a shared view model and it is faster
package io.github.andraantariksa.cratesio.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import io.github.andraantariksa.cratesio.R
import io.github.andraantariksa.cratesio.data.api.model.CrateSummary.CrateSummary
import io.github.andraantariksa.cratesio.utils.InjectorUtils
import kotlinx.android.synthetic.main.fragment_summary.*
import kotlinx.coroutines.launch
class SummaryFragment : ScopedFragment() {
private lateinit var viewModelFactory: CratesViewModelFactory
private lateinit var viewModel: CratesViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_summary, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModelFactory = InjectorUtils.provideCratesViewModelFactory(context!!)
viewModel = ViewModelProvider(this, viewModelFactory)
.get(CratesViewModel::class.java)
viewModel.cratesSummaryLast?.let {
setupSummaryRecyclerView(it)
}
fetchRecyclerViewData()
}
private fun fetchRecyclerViewData() {
launch {
val crateSummaryLiveData = viewModel.crateSummary.await()
crateSummaryLiveData.observe(viewLifecycleOwner, Observer {
setupSummaryRecyclerView(it)
})
}
}
private fun setupSummaryRecyclerView(crateSummary: CrateSummary) {
viewModel.cratesSummaryLast = crateSummary
textViewCratesTotalNum.text = crateSummary.numCrates.toString()
textViewDownloadsTotalNum.text = crateSummary.numDownloads.toString()
val newCratesAdapter =
CrateSummaryRecyclerViewAdapter(crateSummary.newCrates)
recyclerViewNewCrates.layoutManager = LinearLayoutManager(context)
recyclerViewNewCrates.adapter = newCratesAdapter
val mostDownloadedAdapter =
CrateSummaryRecyclerViewAdapter(crateSummary.mostDownloaded)
recyclerViewMostDownloaded.layoutManager = LinearLayoutManager(context)
recyclerViewMostDownloaded.adapter = mostDownloadedAdapter
val justUpdatedAdapter =
CrateSummaryRecyclerViewAdapter(crateSummary.justUpdated)
recyclerViewJustUpdated.layoutManager = LinearLayoutManager(context)
recyclerViewJustUpdated.adapter = justUpdatedAdapter
val mostRecentlyDownloaded =
CrateSummaryRecyclerViewAdapter(crateSummary.mostRecentlyDownloaded)
recyclerViewMostRecentlyDownloaded.layoutManager = LinearLayoutManager(context)
recyclerViewMostRecentlyDownloaded.adapter = mostRecentlyDownloaded
val mostPopularKeywords =
CrateSummaryRecyclerViewAdapter(crateSummary.mostRecentlyDownloaded)
recyclerViewPopularKeywords.layoutManager = LinearLayoutManager(context)
recyclerViewPopularKeywords.adapter = mostPopularKeywords
val popularCategories =
CrateSummaryRecyclerViewAdapter(crateSummary.mostRecentlyDownloaded)
recyclerPopularCategories.layoutManager = LinearLayoutManager(context)
recyclerPopularCategories.adapter = popularCategories
}
}
But, by using a shared view model, it became slower to access/open the fragment
package io.github.andraantariksa.cratesio.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import io.github.andraantariksa.cratesio.R
import io.github.andraantariksa.cratesio.data.api.model.CrateSummary.CrateSummary
import io.github.andraantariksa.cratesio.utils.InjectorUtils
import kotlinx.android.synthetic.main.fragment_summary.*
import kotlinx.coroutines.launch
class SummaryFragment : ScopedFragment() {
// private lateinit var viewModelFactory: CratesViewModelFactory
// private lateinit var viewModel: CratesViewModel
private val viewModel: CratesViewModel by activityViewModels {
InjectorUtils.provideCratesViewModelFactory(requireContext())
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_summary, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
// viewModelFactory = InjectorUtils.provideCratesViewModelFactory(context!!)
// viewModel = ViewModelProvider(this, viewModelFactory)
// .get(CratesViewModel::class.java)
viewModel.cratesSummaryLast?.let {
setupSummaryRecyclerView(it)
}
fetchRecyclerViewData()
}
private fun fetchRecyclerViewData() {
launch {
val crateSummaryLiveData = viewModel.crateSummary.await()
crateSummaryLiveData.observe(viewLifecycleOwner, Observer {
setupSummaryRecyclerView(it)
})
}
}
private fun setupSummaryRecyclerView(crateSummary: CrateSummary) {
viewModel.cratesSummaryLast = crateSummary
textViewCratesTotalNum.text = crateSummary.numCrates.toString()
textViewDownloadsTotalNum.text = crateSummary.numDownloads.toString()
val newCratesAdapter =
CrateSummaryRecyclerViewAdapter(crateSummary.newCrates)
recyclerViewNewCrates.layoutManager = LinearLayoutManager(context)
recyclerViewNewCrates.adapter = newCratesAdapter
val mostDownloadedAdapter =
CrateSummaryRecyclerViewAdapter(crateSummary.mostDownloaded)
recyclerViewMostDownloaded.layoutManager = LinearLayoutManager(context)
recyclerViewMostDownloaded.adapter = mostDownloadedAdapter
val justUpdatedAdapter =
CrateSummaryRecyclerViewAdapter(crateSummary.justUpdated)
recyclerViewJustUpdated.layoutManager = LinearLayoutManager(context)
recyclerViewJustUpdated.adapter = justUpdatedAdapter
val mostRecentlyDownloaded =
CrateSummaryRecyclerViewAdapter(crateSummary.mostRecentlyDownloaded)
recyclerViewMostRecentlyDownloaded.layoutManager = LinearLayoutManager(context)
recyclerViewMostRecentlyDownloaded.adapter = mostRecentlyDownloaded
val mostPopularKeywords =
CrateSummaryRecyclerViewAdapter(crateSummary.mostRecentlyDownloaded)
recyclerViewPopularKeywords.layoutManager = LinearLayoutManager(context)
recyclerViewPopularKeywords.adapter = mostPopularKeywords
val popularCategories =
CrateSummaryRecyclerViewAdapter(crateSummary.mostRecentlyDownloaded)
recyclerPopularCategories.layoutManager = LinearLayoutManager(context)
recyclerPopularCategories.adapter = popularCategories
}
}
So, what makes the fragment are slow to open? And how do I fix it? Am I doing a wrong approach to use the shared view model?
Edit
So I have to run the setupSummaryRecyclerView in asynchronous manner. But it is still slower
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModelFactory = InjectorUtils.provideCratesViewModelFactory(context!!)
viewModel = ViewModelProvider(this, viewModelFactory)
.get(CratesViewModel::class.java)
launch {
viewModel.cratesSummaryLast?.let {
setupSummaryRecyclerView(it)
}
fetchRecyclerViewData()
}
}
Well, it turns out I forget to setupSummaryRecyclerView in asynchronous manner :) . Also, it looks like by activityViewModels is slow, so I make it to run it asynchronously as well.
package io.github.andraantariksa.cratesio.ui
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import io.github.andraantariksa.cratesio.R
import io.github.andraantariksa.cratesio.data.api.model.CrateSummary.CrateSummary
import io.github.andraantariksa.cratesio.utils.InjectorUtils
import kotlinx.android.synthetic.main.fragment_summary.*
import kotlinx.coroutines.launch
class SummaryFragment : ScopedFragment() {
private lateinit var viewModel: CratesViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_summary, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
launch {
val viewModelLateInit by activityViewModels<CratesViewModel> {
InjectorUtils.provideCratesViewModelFactory(requireContext())
}
viewModel = viewModelLateInit
fetchRecyclerViewData()
}
}
private fun fetchRecyclerViewData() {
launch {
viewModel.cratesSummaryLast?.let {
setupSummaryRecyclerView(it, false)
}
val crateSummaryLiveData = viewModel.crateSummary.await()
crateSummaryLiveData.observe(viewLifecycleOwner, Observer {
setupSummaryRecyclerView(it)
})
}
}
private fun setupSummaryRecyclerView(crateSummary: CrateSummary, cache: Boolean = true) {
if (cache) {
viewModel.cratesSummaryLast = crateSummary
}
textViewCratesTotalNum.text = crateSummary.numCrates.toString()
textViewDownloadsTotalNum.text = crateSummary.numDownloads.toString()
val newCratesAdapter =
CrateSummaryRecyclerViewAdapter(crateSummary.newCrates)
recyclerViewNewCrates.layoutManager = LinearLayoutManager(context)
recyclerViewNewCrates.adapter = newCratesAdapter
val mostDownloadedAdapter =
CrateSummaryRecyclerViewAdapter(crateSummary.mostDownloaded)
recyclerViewMostDownloaded.layoutManager = LinearLayoutManager(context)
recyclerViewMostDownloaded.adapter = mostDownloadedAdapter
val justUpdatedAdapter =
CrateSummaryRecyclerViewAdapter(crateSummary.justUpdated)
recyclerViewJustUpdated.layoutManager = LinearLayoutManager(context)
recyclerViewJustUpdated.adapter = justUpdatedAdapter
val mostRecentlyDownloaded =
CrateSummaryRecyclerViewAdapter(crateSummary.mostRecentlyDownloaded)
recyclerViewMostRecentlyDownloaded.layoutManager = LinearLayoutManager(context)
recyclerViewMostRecentlyDownloaded.adapter = mostRecentlyDownloaded
val mostPopularKeywords =
CrateSummaryRecyclerViewAdapter(crateSummary.mostRecentlyDownloaded)
recyclerViewPopularKeywords.layoutManager = LinearLayoutManager(context)
recyclerViewPopularKeywords.adapter = mostPopularKeywords
val popularCategories =
CrateSummaryRecyclerViewAdapter(crateSummary.mostRecentlyDownloaded)
recyclerPopularCategories.layoutManager = LinearLayoutManager(context)
recyclerPopularCategories.adapter = popularCategories
}
}

Firestore error retrieving data using Kotlin

I am trying to retrieve data from the Firebase Firestore using the MVVM pattern . The build gets successfull everytime but the app fails to open up. I am not able to find faults in the code.The application is able to add data but retrieving data is the issue. Please help.Thank-You
ViewModel
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.google.firebase.firestore.FirebaseFirestore
import com.karuneshpalekar.firestorepagination.models.Note
import java.lang.Exception
class DataViewModel : ViewModel() {
companion object{
private const val TAG =" VIEWMODEL"
}
private val db = FirebaseFirestore.getInstance()
private val noteref = db.collection("record")
private val _results = MutableLiveData<Exception>()
val results: LiveData<Exception>
get() = _results
private val _notes = MutableLiveData<List<Note>>()
val notes: LiveData<List<Note>>
get() = _notes
fun addData(note: Note) {
noteref.add(note).addOnCompleteListener {
if (it.isSuccessful) {
Log.w("TAG", "success")
_results.value = null
} else {
_results.value = it.exception
}
}
}
fun fetchData() {
noteref.addSnapshotListener{ snapshot,e->
if (e!=null){
Log.w(TAG,"Listen failed",e)
return#addSnapshotListener
}
if (snapshot != null) {
val items = mutableListOf<Note>()
for (docs in snapshot){
val notes = docs.toObject(Note::class.java)
notes.let {
items.add(it)
}
_notes.value = items
}
}
}
}
}
Adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.karuneshpalekar.firestorepagination.R
import com.karuneshpalekar.firestorepagination.models.Note
import kotlinx.android.synthetic.main.list_item.view.*
class DataAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var items= mutableListOf<Note>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return DataViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
)
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is DataViewHolder -> {
holder.bind(items[position])
}
}
}
fun setNote(items:List<Note>){
this.items = items as MutableList<Note>
notifyDataSetChanged()
}
class DataViewHolder constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val textName = itemView.text_view_name
fun bind(note: Note) {
textName.text = note.name
}
}
}
Fragment
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.karuneshpalekar.firestorepagination.R
import kotlinx.android.synthetic.main.fragment_recyclerview.*
class RecyclerView : Fragment() {
private lateinit var viewModel: DataViewModel
private lateinit var dataadapter : DataAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
viewModel = ViewModelProviders.of(this).get(DataViewModel::class.java)
return inflater.inflate(R.layout.fragment_recyclerview, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
recycler_view.apply {
dataadapter = DataAdapter()
adapter = dataadapter
}
viewModel.fetchData()
viewModel.notes.observe(viewLifecycleOwner, Observer {
dataadapter.setNote(it)
})
floating_btn_add.setOnClickListener {
DialogFragment().show(childFragmentManager, "")
}
}
}
DialogFragment - To add Data
class DialogFragment :DialogFragment(){
private lateinit var viewmodel:DataViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
viewmodel = ViewModelProviders.of(this).get(DataViewModel::class.java)
return inflater.inflate(R.layout.fragment_dialog, container, false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, android.R.style.Theme_DeviceDefault_Light_Dialog_MinWidth)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewmodel.results.observe(viewLifecycleOwner, Observer {
val message = if (it == null) {
getString(R.string.name_added)
} else {
getString(R.string.name_error)
}
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
dismiss()
})
button_add.setOnClickListener {
val names = edit_text_name.text.toString().trim()
val note= Note("",names)
viewmodel.addData(note)
}
}
}
RecyclerView XML
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".ui.RecyclerView">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="#dimen/margin"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="#layout/list_item"
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/floating_btn_add"
app:fabSize="normal"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/margin"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
The above is the code of my Project
The only thing missing in the code that provided errors was not having null-check in the Model class ,i.e., Note Class.

Android: pass data from RecyclerView to another Activity

I have a RecyclerView and I have to pass data to another activity when u click on an item. When someone clicks on the first item I need to pass the data of that item to an activity.
This is the part of the fragment where the recycler view is located:
class TrafficFragment : Fragment() {
private lateinit var trafficViewModel: TrafficViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
trafficViewModel =
ViewModelProviders.of(this).get(TrafficViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_traffic, container, false)
//...
val recyclerViewTraffic: RecyclerView = root.findViewById(R.id.recyclerViewTraffic)
recyclerViewTraffic.apply {
layoutManager = LinearLayoutManager (this#TrafficFragment.context)
Log.d("DEBUG", MainActivity.clickArray.toString())
adapter = TrafficAdapter(MainActivity.clickArray){
var intent = Intent(activity, ClickDetail::class.java)
startActivity(intent)
}
}
return root
}
}
The adapter:
package com.example.example
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.traffic_recycler_layout.view.*
class TrafficAdapter(private val trafficClick: List<TrafficClick>, val clickDetail: () -> Unit) :
RecyclerView.Adapter<TrafficAdapter.ViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutView = LayoutInflater.from(parent.context).inflate(R.layout.traffic_recycler_layout, parent, false)
return ViewHolder(layoutView)
}
override fun getItemCount(): Int = MainActivity.clicksNumber
companion object{
var idClick: String?= String()
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val clicks = trafficClick[position]
holder.view.indirizzoIp.text = "${clicks.ip}"
holder.view.isp.text= "${clicks.organization}"
holder.view.data.text = "${clicks.data_creation}"
holder.view.numberText.text = "${clicks.id_campaign}"
holder.view.setOnClickListener {
clickDetail.invoke()
}
}
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view)
}
and this is the activity that I need to open when I click on the item with the details of that item:
package com.example.example
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.traffic_click_detail.*
class ClickDetail : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.traffic_click_detail)
}
}
var intent = Intent(activity, ClickDetail::class.java)
intent.putExtra("your_key",MainActivity.clickArray.toString())
startActivity(intent)
class ClickDetail : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.traffic_click_detail)
val YourData = getIntent().getStringExtra("your_key")
}
}
If I suppose you want to pass and id, replace this
holder.view.setOnClickListener {
clickDetail.invoke()
}
by
holder.view.setOnClickListener {
ClickDetail.launch(holder.itemView.context, /*your ID*/)
}
Then to retrieve your data
class ClickDetail : AppCompatActivity(){
companion object {
private const val EXTRA_KEY_ID = "CLICKDETAIL.EXTRA_KEY_ID"
fun launch(launcher: Activity, id: Int) {
val intent = Intent(launcher, ClickDetail::class.java)
.apply {
putExtra(EXTRA_KEY_ID, id)
}
launcher.startActivity(intent)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.traffic_click_detail)
val id = intent.getIntExtra("EXTRA_KEY_ID")
}
}

Categories

Resources