Add custom but specifc layout itemdecoration between recyclerview items - android

I have a list of message objects that are being displayed in a recycler view as normal
#Entity(tableName = "messages")
data class Message(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "message_id")
var messageId: String,
#ColumnInfo(name = "body", defaultValue = "")
var body: String,
#ColumnInfo(name = "msg_type")
var msgType: Int = 0,
#ColumnInfo(name = "message_date", defaultValue = "")
var messageDate: String
)
How ever i want to implement a feature like whatsapp, that by scrolling between messages you can see a message date view like "02 May" that appears only if two messages have messageDate a different day. Should i use a customItemDecoration? But again i can't
How can i implement that?

Related

How to time stamp Room database with Long

I'm currently adding date/time columns to my current table and want to timestamp when creating an item and add another column of when an item is edited. I'd like to know of when exactly I should timestamp the item and how. I'm seeing an option with System.currentTimeMillis(), but want to make sure it's best practice.
In my prior table I don't have date/time so need to add default values for them as well.
I currently have my Item class as below:
#Entity
class Item
(#field:ColumnInfo(name = "item_name") #get:Bindable var itemName: String) :
BaseObservable() {
#PrimaryKey(autoGenerate = true)
var uid = 0
#ColumnInfo(name = "is_checked")
var isChecked = false
#ColumnInfo(name = "is_listed")
var isListed = false
#ColumnInfo(name = "listed_at")
var listedAt: Long? = System.currentTimeMillis()
#ColumnInfo(name = "created_at")
var createdAt: Long? = System.currentTimeMillis()
}

Nested RecyclerView Displaying Only One Item

I am using Room database and recylerview to display items in Android(Kotlin). Below is my Entity.
Entity(tableName = "Note")
#Parcelize
data class Product(
#ColumnInfo(name = "Title") val title: String? = null,
#ColumnInfo(name = "Body") val body: String? = null,
#ColumnInfo(name = "Description") val description: ArrayList<String>?,
#ColumnInfo(name = "Completed") val completed: ArrayList<Boolean>?): Parcelable
I am able to display the Parent recycler view properly, but inside it, I am trying to display description and completed as Nested recyclerview, but I am getting only the last item from them, not all of them.
Any help? Thanks in advance.
Screenshot Attached

How to build and query a Room DB to return a list of objects of multiple classes?

Bear with me, it's a tricky question and what resources I've found around don't really help me resolve my problem.
I'm trying to build a real estate-oriented app on Kotlin. It must show at some point a RecyclerView with multiple object classes (say: houses, flats, plots, buildings, etc.)
I've seen multiple examples of RVs designed to accept multiple classes, but I'm struggling to put together a DB and the intermediary classes translating between tables and POJOs.
So far I've figured the following:
I must have a Properties table that stores the unique ID for every object, along with another identifier for its type and a series of values common to every property (say, address, price, etc.)
I must have a table for each entity type that can be independently listed as a real estate item (say, a house, a flat, a plot of land, a building, what have you). Each row on those tables will have a primary foreign key referencing its equivalent on the Properties table.
Now for the unexpected habanero. I decided to start sketching out my project on the basis of the RecyclerView Kotlin codelabs Google put together for newbies like me. Therein data is retrieved from the DB in this fashion:
this.plots = Transformations.map(database.RealtorDao.getPlots()) { it.asDomainModel() }
This works smoothly enough when the objects on the list the DB spits at you are all of one single kind, but what happens if you need them to be of different classes so that the adapter can tell them apart?
Or the only way around is just to build a gigantic table with about a hundred columns that will have nulls everywhere, and sort out objects ONLY AFTER they've been parsed in the previously described fashion?
I smashed my head against this wall until I got tired of hearing the squishing sound. I could not get a Room DB to return a list of objects of multiple classes, so I had to adopt a dirtier approach.
If I had worked just with the database classes then probably I could have hacked it, but trying to translate objects of such classes into POJOs to use instead complicated things somewhat.
The workaround I found was to make a master real estate class and accept that it would have lots and lots of null fields on the database. While a far cry from ideal, it works.
Database object classes:
open class DatabaseProperty
{
#ColumnInfo(name = COL_TYPE)
#SerializedName(COL_TYPE)
#Expose
var type: String? = null
#ColumnInfo(name = COL_ADDRESS)
#SerializedName(COL_ADDRESS)
#Expose
var address: String? = null
#ColumnInfo(name = COL_OWNER)
#SerializedName(COL_OWNER)
#Expose
var owner: String? = null
#ColumnInfo(name = COL_PRICE_FINAL)
#SerializedName(COL_PRICE_FINAL)
#Expose
var priceFinal: Long? = null
#ColumnInfo(name = COL_PRICE_QUOTED)
#SerializedName(COL_PRICE_QUOTED)
#Expose
var priceQuoted: Long? = null
/**
* No args constructor for use in serialization
*/
constructor()
#Ignore
constructor
(
type: String,
address: String,
owner: String,
priceFinal: Long,
priceQuoted: Long
) : super() {
this.type = type
this.address = address
this.owner = owner
this.priceFinal = priceFinal
this.priceQuoted = priceQuoted
}
}
#Entity
(
tableName = TABLE_RE,
indices =
[
Index(value = [COL_RE_ID], unique = true)
],
foreignKeys =
[
ForeignKey
(
entity = DatabaseRealEstate::class,
parentColumns = arrayOf(COL_RE_ID),
childColumns = arrayOf(COL_PARENT_ID),
onDelete = ForeignKey.NO_ACTION
)
]
)
data class DatabaseRealEstate
(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = COL_RE_ID)
var id: Int? = null,
#ColumnInfo(name = COL_PARENT_ID)
var parent_id: Int? = null,
#Embedded(prefix = RE)
var property: DatabaseProperty? = null,
#ColumnInfo(name = COL_PARCEL_FRONT) // Plot front
#SerializedName(COL_PARCEL_FRONT)
#Expose
var front: Float? = null,
#ColumnInfo(name = COL_PARCEL_SIDE) // Plot side
#SerializedName(COL_PARCEL_SIDE)
#Expose
var side: Float? = null,
#ColumnInfo(name = COL_AREA) // Plot area
#SerializedName(COL_AREA)
#Expose
var area: Float? = null,
#ColumnInfo(name = COL_CATASTER)
#SerializedName(COL_CATASTER)
#Expose
var cataster: String? = null,
#ColumnInfo(name = COL_ZONIFICATION)
#SerializedName(COL_ZONIFICATION)
#Expose
var zonification: String? = null,
)
data class RealEstateWithSubunits
(
#Embedded
val re: DatabaseRealEstate? = null,
#Relation
(
parentColumn = COL_RE_ID,
entityColumn = COL_PARENT_ID,
entity = DatabaseRealEstate::class
)
var subunits: List<DatabaseRealEstate>? = null,
#Relation
(
parentColumn = COL_RE_ID,
entityColumn = COL_PARENT_ID,
entity = DatabaseChamber::class
)
var chambers: List<DatabaseChamber>? = null
)
fun List<RealEstateWithSubunits>.asRESUBDomainModel() : List<RealEstate>
{
return map { obj ->
RealEstate(
id = obj.re!!.id!!,
type = obj.re.property!!.type!!,
address = obj.re.property!!.address!!,
owner = obj.re.property!!.owner!!,
priceFinal = obj.re.property!!.priceFinal!!,
priceQuoted = obj.re.property!!.priceQuoted!!,
parent_id = obj.re.parent_id,
front = obj.re.front,
side = obj.re.side,
area = obj.re.area,
cataster = obj.re.cataster,
zonification = obj.re.zonification,
chambers = obj.chambers!!.asChamberDomainModel(),
subunits = obj.subunits!!.asREDomainModel()
)
}
}
fun List<DatabaseChamber>.asChamberDomainModel(): List<Chamber>
{
return map {
Chamber(
id = it.id,
parent_id = it.parent_id,
front = it.front,
side = it.side,
area = it.area
)
}
}
fun List<DatabaseRealEstate>.asREDomainModel(): List<RealEstate>
{
return map { obj ->
RealEstate(
id = obj.id!!,
type = obj.property!!.type!!,
address = obj.property!!.address!!,
owner = obj.property!!.owner!!,
priceFinal = obj.property!!.priceFinal!!,
priceQuoted = obj.property!!.priceQuoted!!,
parent_id = obj.parent_id,
front = obj.front,
side = obj.side,
area = obj.area,
cataster = obj.cataster,
zonification = obj.zonification,
chambers = ArrayList(),
subunits = ArrayList()
)
}
}
Model object classes:
interface BaseProperty {
var id: Int
var type: String
var address: String
var owner: String
var priceFinal: Long
var priceQuoted: Long
}
data class RealEstate(
override var id: Int = -1,
override var type: String = "",
override var address: String = "",
override var owner: String = "",
override var priceFinal: Long = 0,
override var priceQuoted: Long = 0,
var parent_id: Int?,
var front: Float?,
var side: Float?,
var area: Float?,
var cataster: String?,
var zonification: String?,
var subunits: List<RealEstate>? = null,
var chambers: List<Chamber>? = null
) : BaseProperty
{
fun hasParent() : Boolean
{
if (parent_id == null)
{
return false
}
return true
}
}
I haven't yet found a better approach, so if someone does, I'm welcoming it with open arms.

How to autogenerate a Room database id without providing an id

I am trying to create a room database and I want each item inserted into it to have its own unique id without me having to provide it, The problem is when I try to insert new items into the database I get an error asking me to provide an id.
Here is my entity:
#Entity(tableName = "notes_table")
data class Note(
#PrimaryKey(autoGenerate = true)
val id: Int = 0,
#ColumnInfo(name = "description")
val description: String,
#ColumnInfo(name = "priority")
var priority: Int)
Is there a way to have the database create its own auto-generated auto-increasing id column without having me having to add it like this:
val item = Note(id, item, priority)
insert(item)
And instead do this:
val item = Note(item, priority)
insert(item)
Create a constructor that takes item and priority as arguments
#Entity(tableName = "notes_table")
data class Note (var item: String,
#ColumnInfo(name = "priority")
var priority: String) {
#PrimaryKey(autoGenerate = true)
var id: Long = 0,
//.....
}
You can just simply give the id a default value and put that at the end:
#Entity(tableName = "notes_table")
data class Note(
#ColumnInfo(name = "description")
val description: String,
#ColumnInfo(name = "priority")
var priority: Int)
#PrimaryKey(autoGenerate = true) //must be at the end
val id: Int = 0 //Long type recommend
)
Then you can:
val item = Note(item, priority)
insert(item)
Because your data class Note has three parameter.
So you you have to create Note by passing three parameter.
It is nothing to do with autogenerate or room.

Why do I have to make a setter for an auto-incremented column in a database?

In my android app I have created a database using the room persistance library. It contains of a table called stack with the columns stack_id, col_1, col_2 and stack_name. Here's the code for the entity class:
#Entity
class Stack(
#ColumnInfo(name = "stack_name") val stackName: String,
#ColumnInfo(name = "col_1") val column1: String,
#ColumnInfo(name = "col_2") val column2: String
) {
#PrimaryKey(autoGenerate = true) #ColumnInfo(name = "stack_id") val
stackId: Int = 0
}
Question 1: Is the stack_id correctly implemented? I found this solution to set en auto-incremented column to 0 and it will automaticly auto-increment the value, but it doesn't make sense to me. So, is this correct?
Question 2: Whe I want to build the app, it throws the error:
Cannot find setter for field. private final int stackId = 0;
But it would be nonsense to make a setter for an auto-incremented value. So, should I make a setter or is there another solution?
You are using concrete class so you need to provide get() and set() for properties.
Use data class which are build for this purpose only .
#Entity
data class Stack(
#PrimaryKey(autoGenerate = true) #ColumnInfo(name = "stack_id") val id: Int? = null,
#ColumnInfo(name = "stack_name") val stackName: String,
#ColumnInfo(name = "col_1") val column1: String,
#ColumnInfo(name = "col_2") val column2: String)
You need to move
#PrimaryKey(autoGenerate = true) #ColumnInfo(name = "stack_id") val
stackId: Int = 0
to the constructor.
#Entity
class Stack(
#PrimaryKey(autoGenerate = true) #ColumnInfo(name = "stack_id") val
stackId: Int = 0,
#ColumnInfo(name = "stack_name") val stackName: String,
#ColumnInfo(name = "col_1") val column1: String,
#ColumnInfo(name = "col_2") val column2: String
)
Why this works? The property is val, so is final/immutable, but the assignment in the constructor is a default value which can be overridden by passing in a value.
However in your example the property is in the class body and is assigned the value 0 on init, and cannot be changed as property is immutable.
The second option, as pointed out, is making the property mutable by using var instead of val.

Categories

Resources