Kotlin: How can inflate Room data into a RecyclerView using Fragments? - kotlin

I trying use Room to inflate the Database in an RecyclerView, but all this using Fragments. I have the screen where the user enters the data and the screen where the RecyclerView is inflated, but I don't know how to make the Room data inflate in the RecyclerView and I still can't understand the viewModel.

For Getting data from Room this might get helpful
and for view modal this helped me.
after getting data from Room you can simply notify the adapter.
example :
var myList = ArrayList<YourModel>()
roomDataList.forEach { item ->
//You can parse or convert your data according to your use
myList.add(item)
}
adapter.updateList(myList)

Related

Adapter with Picasso

I'm trying to use Picasso to get images from Url and put inside an Adapter. I have to send the image as a parameter to the funcion imageSlider inside the my SliderViewHolder. Can someone explain me how?
Picasso get:
Picasso.get()
.load(sliderImages[position])
.transform( RoundedTransformation(30, 0))
.placeholder(context.resources.getDrawable(R.drawable.ic_launcher_foreground))//it will show placeholder image when url is not valid.
.networkPolicy(NetworkPolicy.OFFLINE) //for caching the image url in case phone is offline
.into(holder.imageSlider(?))
Viewholder:
class SliderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: RoundedImageView = itemView.findViewById(R.id.image_slide)
fun imageSlider(sliderItem: SliderItem) {
imageView.setImageResource(sliderItem.image)
}
}
The Function
fun imageSlider(sliderItem: SliderItem) {
imageView.setImageResource(sliderItem.image)
}
is not necessary, because you declare the imageView with a public visibility.
In the Adapter of your RecyclerView, there is a Method called
override fun onBindViewHolder(holder: SliderViewHolder, position: Int)
This one will get invoked, when the Adapter wants to bind your Item Layout (ViewHolder) with a "backing data item" of the List...
Then you could simply do this:
Picasso.get()
.load(sliderImages[position])
.transform( RoundedTransformation(30, 0))
.placeholder(context.resources.getDrawable(R.drawable.ic_launcher_foreground))//it will show placeholder image when url is not valid.
.networkPolicy(NetworkPolicy.OFFLINE) //for caching the image url in case phone is offline
.into(holder.imageView)
Which will make Picasso, load the Image into the ImageView of your ViewHolder (which actually simply holds the Item Layout) for the current Item in the List.
The correct implementation would actually be, that you give your Adapter a List of Image Objects (for example, containing Properties like Image Name and a Path/URL, that should get "rendered") and when the Adapter is invoking "onBindViewHolder" with the corresponding Position, it is your Job to implement the loading of the image from the given path for the given position.

RecyclerView Spinner showing data for all the rows instead of only one row at a time

I have two spinners in my RecyclerView. My first spinner is filled inside of the xml ->
<Spinner
android:id="#+id/itemspnReason"
android:layout_width="600dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:entries="#array/list_reasons"
/>
How ever with my second spinner it is not so easy. The spinner will show a number between 1 and the quantity available per item showed in the RecyclerView. Currently I have this code inside my onBindViewHolder ->
val modal = partsList[position]
holder.apply {
var i = 1
while (i <= modal.PQTY){ adapter.add(i.toString())
i++}
itemspnQTY.adapter = adapter
}
It works in getting to the desired number. The problem is each spinner is filled with all the data from each entry into the RecyclerView. For example if my first item had a size of 2 and my second item had a size of 4. Each of my spinner will contain the following: 1, 2, 1, 2, 3, 4.
So I need a way to clear the data after each entry or a better way to populate my spinner. My spinner is inside my item_list.xml for my RecyclerView. The easy solution is to do the same as my other spinner and create a static/hardcode entry. However I would like my spinner size to change based on how many items are available.
I have tried putting it inside my onCreateViewHolder and init. It did not solve problem.
I solved my problem. I found help from a different post and implemented it with my app. (How to make Dynamic Spinner inside RecyclerView?)
override fun onBindViewHolder(holder: RowViewHolder, position: Int) {
val modal = partsList[position]
var i = 1
val spnlist: MutableList<Int> = ArrayList()
while (i <= modal.PQTY){ spnlist.add(i)
i++}
holder.itemspnQTY.adapter =
ArrayAdapter(context, android.R.layout.simple_spinner_dropdown_item, spnlist)
}

When updating/deleting entry in RoomDatabase and then creating new entry App crashes

I work on an app where I have a recyclerview and a fab on the first fragment (listFragment) you see when you open the app. you click the fab, camera opens, you make a picture -> click ok and get to another fragment, there you fill out some stuff and then press save. you get back to the recyclerview fragment and now you can see the saved entry (room database). when you click the listItem you created before you get to an update fragment where you can either update stuff or delete it. now before it all worked fine.
But now with help of a yt video I tried to also insert a bitmap into the database. That works (#TypeConverter) and now I also have the right bitmap displayed in the listItem and the updateFragment.
The problem:
When I click on a listItem and in the updateFragment I click the update button or delete icon that works fine and I get back to the listFragment. But when I then click on the fab in order to create a new entry the camera opens and then the app crashes (and the camera stays open).
Even when the delete and update code is commented out and the only thing that happens when you click these buttons is the navigation between fragments it still crashes but if I remove all the bitmap database code it is fine again. I can delete and/or update multiple entries no problem but when I click the fab and camera starts the app crashes. I also use safeargs for transportation of data to updateFragment when I click the listItem. The app also crashes when I delete/update something and then leave the app by pressing the homebutton on the phone. I navigate out of the app and then get the "app crashed" message.
LOGCAT:
java.lang.RuntimeException: android.os.TransactionTooLargeException:
data parcel size 4675628 bytes
I also found out that if I delete/update multiple entries in a row and then click the fab and the camera opens the amount of bytes multiply. For the following number to be reached I deleted 5 listItems in a row:
TransactionTooLargeException: data parcel size 23309336 bytes
I just wonder what type of data that is and why does it stack up for every navigation between my updateFragment and listFragment(as even when the code for deleting and updating is removed that still happens)? And why does it then become a problem when I start the camera or close the app per homebutton? Maybe contrary to my original believe the bitmap being display in the listItems and the updateFragment are somehow the problem. I display high quality bitmaps by using a fileprovider because in the addFragment and the updateFragment the imageViews are rather big and i use the same bitmap for the small imageViews on the listItems.
If anyone has any idea why that might happen I would be super gratefull!
Author here:
Found an answer to my problem even though I still don't completely get it.
Instead of using a #TypeConverter to turn a bitmap into a byteArray on storing it in the database and on retrieving turning it form byteArray back to bitmap i now store a uri as a string. I found that code which turns a bitmap into a uri and stores the bitmap on the device and it works like a charm:
private fun saveImageToInternalStorage(bitmap: Bitmap): Uri {
val wrapper = ContextWrapper(applicationContext)
var file = wrapper.getDir(IMAGE_DIRECTORY, Context.MODE_PRIVATE)
file = File(file, "${UUID.randomUUID()}.jpg")
try{
val stream : OutputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream)
stream.flush()
stream.close()
}catch (e: IOException){
e.printStackTrace()
}
return Uri.parse(file.absolutePath)
}
companion object{
private const val IMAGE_DIRECTORY = "...Images"
}

How to implement left and right swipe to reveal buttons in recycler view?

I need to implement left and right buttons in recycler view. I need to have 3 buttons in left side and right side. Also when say bottons like Pin, Edit etc. Something like below images.
I have implemented this using item decoration of recyclerview
val itemTouchHelper = ItemTouchHelper(swipeController)
itemTouchHelper.attachToRecyclerView(recyclerView)
recyclerView?.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
swipeController.onDraw(c)
}
})
When I select favorite in a row that state needs to be saved and when I swipe that particular cell again it should be highlighted to show that it is selected. I am not able to implement this. Please let me know if there is some alternative solution to implement this.
Thanks

Update RecyclerView adapter after SharedPrefereces changed

I have RecyclerView with custom RecyclerView adapter.
Each row of RecyclerView "opens"(when user clicked on it) using CardFlipAnimation(AnimatorSet).
In each row there is a button that change int value in SharedPreferences , and TextView that shows this value.
If i have opened more than one row i need dynamically change all TextViews in all rows which are opened.
NotifyDataSetChanged and etc updates data in RecyclerView great but the is a problem that they close opened rows(because for standart thay are closed and then open with AnimatorSet)
My main mission now is to understand how to update only one TextView in all opened rows and to keep them opened(dont touch AnimatorSet).
I think i can use an Observer,but i didn't know how correctly implement him.
If you any ideas please help me.
Thanks for answers
Ok, finaly i got a solution , it's a little bit ugly but it works exactly as I need.
First: I pass my RecyclerView to Adapter
...
public CardBaseAdapter(RecyclerView recyclerView,...) {
...
this.mRecyclerView = recyclerView;
....
Second: Create a method in my Adapter :
public void updateViews() {
for(int i =0;i<getItemCount();i++){
HeaderViewHolder holder = (HeaderViewHolder)recyclerView.findViewHolderForAdapterPosition(i);
if(holder!=null){
//Update views at holders
}
}
}
Third: Call this function when i need to update view
P.S. I'm not sure that this is the correct way to update view's , but it works, if you know another, good solution , post it pls