I Can't adapt RecyclerView in RecyclerView at Fragment - android

Using kotlin.
I want to make childRecyclerView in parentRecyclerView.
Maybe I think this problem is from recyclerView adapter.
Data is getting from Firebase. (Cloud Firestore)
I completed 'parent RecyclerView'.
And I also wrote adapting code for childRecyclerView at parentRecyclerView's adapter.
In parentRecyclerView, I asked to receive childRecyclerView's Firebase Data
And I checked through the log that this data was well received.
But my app show only parentRecyclerView's content.
This code is adapting code for ChildRecyclerView in ParentRecyclerView's adapter.
And I write this code at ParentRecyclerView's bind() function. This fun is also contain showing ParentRecyclerView's content(Like Glide for parentRecyclerView's ImageView).
val db = FirebaseFirestore.getInstance()
val queryText2: Query = db.collection("spiceTable")
var queryText3: Query
val storageRef2 = Firebase.storage.reference.child("scentnote/spiceimage")
var j: Int = 1
var detailSpiceList = mutableListOf<SpiceDetailValue>()
queryText2.get().addOnSuccessListener { documents ->
//Loop in parentRecyclerView's content
.addOnSuccessListener { documentx ->
//Getting ChildRecyclerView's content from Firebase
}.addOnCompleteListener {
nAdapter = context?.let { NoteSubRecyclerviewAdapter(it, detailSpiceList) }
fragment2NoteParentItemBinding?.fragment2NoteChildList?.adapter = nAdapter
val gridLayoutManager = GridLayoutManager(context, 4)
fragment2NoteParentItemBinding?.fragment2NoteChildList?.layoutManager = gridLayoutManager
}
detailSpiceList = mutableListOf<SpiceDetailValue>()
From ".addOnCompleteListener", To "~gridLayoutManager}" code is same with adapting ParentRecyclerView at my Fragment. (in code, fragment2Note)
I try to Log at end of addOnSuccessListener and start of addOnCompleteListener.
And addOnCompleteListener's Log is shown faster than addOnSuccessListener's Log.
I don't know why this code work like this.
And also i don't know why this adapter can't work.
At Logcat,
No adapter attached; skipping layout
this log is shown several time but i don't know their reason.
Please help...
And I'm Sorry because I'm not good at English

Please do like this
Declare adapter at the first stage with empty data.
val db = FirebaseFirestore.getInstance()
val queryText2: Query = db.collection("spiceTable")
var queryText3: Query
val storageRef2 = Firebase.storage.reference.child("scentnote/spiceimage")
var j: Int = 1
var detailSpiceList = mutableListOf<SpiceDetailValue>()
nAdapter = context?.let { NoteSubRecyclerviewAdapter(it, detailSpiceList) }
fragment2NoteParentItemBinding?.fragment2NoteChildList?.adapter = nAdapter
val gridLayoutManager = GridLayoutManager(context, 4)
fragment2NoteParentItemBinding?.fragment2NoteChildList?.layoutManager = gridLayoutManager
queryText2.get().addOnSuccessListener { documents ->
//Loop in parentRecyclerView's content
.addOnSuccessListener { documentx ->
//Getting ChildRecyclerView's content from Firebase
// After getting data, you asssign data to adapter
adapter.setItem(detailSpiceList); // You need to create function to attach data to adapter. (it is easy and common way)
}.addOnCompleteListener {
}

Related

Item is not updating if an attribute is changed (Jetpack Compose)

I am drawing a list of devices
#Composable
fun DeviceListScreen(){
val model: DeviceListViewModel = hiltViewModel()
val myDevices: List<MyDevice> by model.myDevices.observeAsState(emptyList())
for(device in myDevices)
Device(device)
}
In model I have a livedata
private val items: List<MyDevice> = ArrayList()
private val _myDevices = MutableLiveData<List<MyDevice>> (emptyList())
val myDevices: LiveData<List<MyDevice>> = _myDevices
I change content of an item then update live data
items[0].signal = 54
_myDevices.value = items
However data is not updating in ui.
I guess this is because the pointer to list was not changed and number of items in the list also is not changes and thus compose does not update this data.
Update 12-08-2022
I've just encountered the same problem you had. I have successfully solved it, so the following solution that I have adapted for your problem should also work fine:
fun updateSignal(idOfSelectedDevice: Int) {
_myDevices.value = myDevices.value?.map { device ->
if (device.id == idOfSelectedDevice) device.copy(
signal = <YOUR_DESIRED_VALUE>
) else device
}
}
You could try wrapping items in its own data class:
data class ItemsList(val devices: List<MyDevice> = ArrayList())
Then change items to private val items: ItemsList = ItemsList()
Since you are using your own data class for items, you can then access the copy function which copies an existing object into a new object and should therefore trigger the update of the liveData object:
_myDevices.value = _myDevices.value?.copy(items = items.devices.apply {
this[0].signal = 54
})

Where do I put a addValueEventListener that only gets triggered when there's a change in my Firebase Realtime Database for my fragment?

In my Fragment for my Android app, I'm using Firebase Realtime Database and Moshi to save and load the data I get from my RecyclerView.
These are the functions I use for this task:
private fun saveData() {
val moshi = Moshi.Builder().add(BigDecimalAdapter).add(KotlinJsonAdapterFactory()).build()
val listMyData = Types.newParameterizedType(List::class.java, ItemCard::class.java)
val jsonAdapter: JsonAdapter<ArrayList<ItemCard>> = moshi.adapter(listMyData)
val json = jsonAdapter.toJson(dataList)
userInfo.child("jsonData").setValue(json)
}
private fun loadData(json: String) = lifecycleScope.launch(Dispatchers.IO) {
if (json != "") {
val type: Type = object : TypeToken<List<ItemCard>>() {}.type
val moshi = Moshi.Builder().add(BigDecimalAdapter).add(KotlinJsonAdapterFactory()).build()
val jsonAdapter: JsonAdapter<ArrayList<ItemCard>> = moshi.adapter(type)
dataList = jsonAdapter.fromJson(json)!!
if (dataList == null) {
dataList = arrayListOf<ItemCard>()
}
}
}
private fun buildRecyclerView() {
recyclerView = rootView.findViewById(R.id.main_recycler_view)
recyclerView.setHasFixedSize(true)
recyclerViewLayoutManager = LinearLayoutManager(this#Main.requireContext())
adapter = MainAdapter(dataList, this)
recyclerView.layoutManager = recyclerViewLayoutManager
recyclerView.adapter = adapter
}
In my onViewCreated, I having this for loading the data and building the RecyclerView:
userInfo.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (postSnapshot in dataSnapshot.children) {
when (postSnapshot.key) {
"jsonData" -> {
loadData(postSnapshot.value.toString())
buildRecyclerView()
}
}
}
}
override fun onCancelled(error: DatabaseError) {}
})
Everything works as I want, however, there's a delay/lag when I go to this specific fragment. There are a total of three fragments in my app. The other two work smoothly with no delay/lag, but when I click on the button or slide the screen to go to this fragment, there's a delay in the change of the UI.
What can I do to make the performance better? Where should I put my addValueEventListener? I only want it to get triggered when the fragment is first created and when the jsonData child gets changed. I believe in my onViewCreated the listener is being triggered multiple times. Is there anything else I can add to my code or modify to make the performance better when saving and loading the RecyclerView data?
when you create a listerner in onViewCreated run then make sure that you remove listener when fragment is not attach.Realtime event listerner return a string that you can easily = to your pojo class like:
ItemCard message = messageSnapshot.getValue(ItemCard.class);
this way is to saving manual converting the list effort.Last important thing that if recyclerview is initialize then don't initialize when data change only notifyDataSetChange.when you adding data in the list then make sure that the
list.clear();
otherwise you data is duplicate because on addValueEventListener return the whole data.

using Recyclerview in fragment and coroutines not work

hi im using recyclerview in a fragment and coroutine, but recyclerview not update and is empty
this is my code that fetch data from web and i wrote that in OnCreateView
launch {
val operation = async(Dispatchers.IO) {
Log.d("asdasdasd", "start")
var doc: Document = Jsoup.connect("http://5743.zanjan.medu.ir").timeout(0).maxBodySize(0).ignoreHttpErrors(true).get()
val table: Elements = doc.select("table[class=\"table table-striped table-hover\"]")
for (myTable in table) {
val rows: Elements = myTable.select("tr")
for (i in 1 until rows.size) {
val row: Element = rows.get(i)
val cols: Elements = row.select("td")
val href: Elements = row.select("a")
val strhref: String = href.attr("href")
itemsData.add(CircularModel(cols.get(2).text(),strhref,"2019",""))
}
}
}
operation.await() // wait for result of I/O operation without blocking the main thread
// update views
activity?.runOnUiThread{
adapter = CircularAdapter(itemsData, this#CircularFragment)
adapter.notifyDataSetChanged()
rc.adapter = adapter
}
}
i checked and itemsData isnt empty but i dont know why recyclerview not updated
Try using withContext(Dispatchers.Main) instead of activity?.runOnUiThread{ syntax to access Main thread from the coroutine
Try to call adapter.notifyDataSetChanged() after setting adapter to your Recycler

Recyclerview not updating in coroutine

hi I'm getting information from web with jsoup and coroutine and I want to show data in recyclerview
All the information is well received but the RecyclerView does not show anything and the view is not updated
fun myCoroutine(): ArrayList<DataModel> {
val listx = arrayListOf<DataModel>()
GlobalScope.launch { // launch new coroutine in background and continue
Log.d("asdasdasd", "start")
var doc: Document = Jsoup.connect("http://5743.zanjan.medu.ir").timeout(0).maxBodySize(0).ignoreHttpErrors(true).sslSocketFactory(setTrustAllCerts()).get()
val table: Elements = doc.select("table[class=\"table table-striped table-hover\"]")
for (myTable in table) {
val rows: Elements = myTable.select("tr")
for (i in 1 until rows.size) {
val row: Element = rows.get(i)
val cols: Elements = row.select("td")
val href: Elements = row.select("a")
val strhref: String = href.attr("href")
listx.add(DataModel(cols.get(2).text(),strhref))
Log.d("asdasf",cols.get(2).text())
}
}
}
return listx
}
private fun getData() {
itemsData = ArrayList()
itemsData = myCoroutine()
adapter.notifyDataSetChanged()
adapter = RVAdapter(itemsData)
}
and this is oncreate
var itemsData = ArrayList<DataModel>()
adapter = RVAdapter(itemsData)
val llm = LinearLayoutManager(this)
itemsrv.setHasFixedSize(true)
itemsrv.layoutManager = llm
getData()
itemsrv.adapter = adapter
This code has numerous bugs (getData, for instance, never sets the adapter onto the RecyclerView), but the biggest issue is that you're not actually waiting for listx to be populated - you're returning it immediately before it's populated. You need to either move the population of the adapter to the coroutine and run that part on the UI thread dispatcher, or use a callback, or dispatch it to the UI thread. Launching a coroutine and returning immediately doesn't make the data get populated when something tries to use it.

RecyclerView not showing elements

I recently started a new project on android studio. I switched to Kotlin and this language is giving me such a hard time!
I've set up a RecyclerView in my app and it's working fine. My adapter takes an ArrayList as an argument and displays all the data.
I have two functions here that create the ArrayList for my adapter: cardMaker() and getEventsInfo(). These two functions return an ArrayList with Cards elements.
When I call my adapter with an ArrayList created by getEventsInfo then all the events are showing fine.
But when I use cardMaker(), there are no events showing up!
I really can't understand what is going on here and it drives me crazy! ^^
If this language is really sequential by default, how is this behavior possible? I fell like I am missing something important here.
private fun cardMaker(): ArrayList<Card?> {
var newCards: ArrayList<Card?> = arrayListOf()
newCards.add(Card("UserCard", R.mipmap.logo_zenith_round, userData.firstName, userData.lastName))
val infoCards = getEventsInfo()
newCards.addAll(infoCards)
return newCards
}
private fun getEventsInfo(): ArrayList<Card?> {
var infoCards: ArrayList<Card?> = arrayListOf()
db.collection("Events")
.get()
.addOnSuccessListener { result ->
for (document in result) {
val eventsInfo = Card("EventCard",
R.mipmap.logo_zenith_round,
"${document.get("Name")}",
"${document.get("Date")}")
infoCards.add(eventsInfo)
}
}
.addOnFailureListener { exception ->
Log.w(TAG, "Error getting documents.", exception)
}
return infoCards
}
cards = cardMaker()
// RecyclerView
linearLayoutManager =
androidx.recyclerview.widget.LinearLayoutManager(this)
recyclerView = findViewById(R.id.recycler_view)
recyclerView.layoutManager = linearLayoutManager
adapter = RecyclerAdapter(cards)
recyclerView.adapter = adapter
Well, it was not related to Kotlin specificity.
As EpicPandaForce said: "Firebase fetches things asynchronously"
So I made a class instance of the variable cards: ArrayList and added adapter.notifyDataSetChanged() in my getEventsInfo() method inside the OnSuccessListener.
It is now working perfectly.
Thank you!

Categories

Resources