'ROOM: how to display one to many relation data into recyclerview using lifecycle
I've tried to display the join data from two entity into the recyclerview. However, when I add new data that has the same idBarang (FK) as the old data that has the same idBarang, recyclerview display the same data row.
This is the code snippet:
ENTITY
@Entity(
foreignKeys = [
ForeignKey(entity = Item::class, parentColumns = ["id"], childColumns = ["id_barang"])
],
indices = [Index("id_barang")]
)
data class Purchaseorder(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
@ColumnInfo(name = "no_po")
val noPO: String,
@ColumnInfo(name = "id_barang")
val idBarang: Int,
@ColumnInfo(name = "tanggal_po")
val tanggalPO: String,
@ColumnInfo(name = "jumlah_po")
val jumlahPO: Int,
@ColumnInfo(name = "harga_po")
val hargaPO: Double
)
@Entity
data class Item(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
@ColumnInfo(name = "kode_barang")
val kodeBarang: String,
@ColumnInfo(name = "nama")
val namaBarang: String,
@ColumnInfo(name = "min_stok")
val minStok: Int,
val stok: Int,
val unit: String
)
data class PurchaseorderAndItem(
@Embedded val item: Item,
@Relation(
parentColumn = "id",
entityColumn = "id_barang"
)
val purchaseorder : List<Purchaseorder>,
// val purchaseorder : Purchaseorder,
)
/**
* Returns the passed in price in currency format.
*/
fun Purchaseorder.getFormattedPrice(): String =
NumberFormat.getCurrencyInstance().format(hargaPO)
fun Purchaseorder.getUnitPrice(): String =
NumberFormat.getCurrencyInstance().format(hargaPO/jumlahPO)
fun Purchaseorder.getLongDate() : String {
val month = arrayOf("Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember")
val dateTemp = tanggalPO.split("/")
return dateTemp[0] + " " +month[dateTemp[1].toInt()-1] + " " +dateTemp[2]
}
DAO
@Dao
interface PurchaseorderDao {
@Transaction
@Query("SELECT * from purchaseorder as po INNER JOIN item ON item.id = po.id_barang WHERE po.id = :id")
fun retrievePurchaseorder(id: Int): Flow<PurchaseorderAndItem>
@Transaction
@Query("SELECT * FROM purchaseorder as po INNER JOIN item ON item.id = po.id_barang ORDER BY po.id DESC")
fun getPurchaseorders(): Flow<List<PurchaseorderAndItem>>
}
VIEWMODEL
class PurchaseorderViewModel(private val purchaseorderDao: PurchaseorderDao) : ViewModel() {
var allPurchaseorders: LiveData<List<PurchaseorderAndItem>> = purchaseorderDao.getPurchaseorders().asLiveData()
fun retrievePurchaseorder(id: Int): LiveData<PurchaseorderAndItem> {
return purchaseorderDao.retrievePurchaseorder(id).asLiveData()
}
...
class PurchaseorderViewModelFactory(
private val purchaseorderDao: PurchaseorderDao
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(PurchaseorderViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return PurchaseorderViewModel(purchaseorderDao) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
LIST FRAGMENT
class PurchaseorderListFragment : Fragment() {
private val viewModel: PurchaseorderViewModel by activityViewModels {
PurchaseorderViewModelFactory(
(activity?.application as InventoryApplication).database.purchaseorderDao(),
)
}
private var _binding: FragmentPurchaseorderListBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
_binding = FragmentPurchaseorderListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val adapter = PurchaseorderListAdapter {
val action =
PurchaseorderListFragmentDirections.actionPurchaseorderListFragmentToPurchaseorderDetailFragment(it.purchaseorder[0].id)
this.findNavController().navigate(action)
}
binding.recyclerViewPurchaseorder.layoutManager = object : LinearLayoutManager(this.context) {
override fun checkLayoutParams(lp: RecyclerView.LayoutParams): Boolean {
lp.width = width
return true
}
}
binding.recyclerViewPurchaseorder.adapter = adapter
viewModel.allPurchaseorders.observe(this.viewLifecycleOwner) { purchaseorder ->
purchaseorder.let {
adapter.submitList(it)
}
}
binding.floatingActionButton.setOnClickListener {
val action = PurchaseorderListFragmentDirections.actionPurchaseorderListFragmentToAddPurchaseorderFragment(
getString(R.string.add_fragment_title)
)
this.findNavController().navigate(action)
}
}
}
LIST ADAPTER
class PurchaseorderListAdapter(private val onPurchaseorderClicked: (PurchaseorderAndItem) -> Unit) :
ListAdapter<PurchaseorderAndItem, PurchaseorderListAdapter.PurchaseorderViewHolder>(DiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PurchaseorderViewHolder {
return PurchaseorderViewHolder(
PurchaseorderListItemBinding.inflate(
LayoutInflater.from(
parent.context
)
)
)
}
override fun onBindViewHolder(holder: PurchaseorderViewHolder, position: Int) {
val current = getItem(position)
holder.itemView.setOnClickListener {
onPurchaseorderClicked(current)
}
holder.bind(current)
}
class PurchaseorderViewHolder(private var binding: PurchaseorderListItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(purchaseorder: PurchaseorderAndItem) {
val purchaseorders = purchaseorder.purchaseorder[0]
val item = purchaseorder.item
binding.rvNamaBarangNNoPO.text = binding.rvNamaBarangNNoPO.context.getString(R.string.kode_cate,item.namaBarang,purchaseorders.noPO)
binding.rvHargaPO.text = purchaseorders.getFormattedPrice()
binding.rvJumlahPO.text = purchaseorders.jumlahPO.toString()
binding.rvTanggalPO.text = purchaseorders.tanggalPO
// Log.i("ininya: ", purchaseorder)
}
}
companion object {
private val DiffCallback = object : DiffUtil.ItemCallback<PurchaseorderAndItem>() {
override fun areItemsTheSame(
oldItem: PurchaseorderAndItem,
newItem: PurchaseorderAndItem,
): Boolean {
return oldItem.purchaseorder[0].id == newItem.purchaseorder[0].id
}
override fun areContentsTheSame(
oldItem: PurchaseorderAndItem,
newItem: PurchaseorderAndItem,
): Boolean {
return oldItem.purchaseorder[0].noPO == newItem.purchaseorder[0].noPO
}
}
}
}
XML RECYCLER ROW
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="horizontal"
android:weightSum="5.7">
<TextView
android:id="@+id/rv_namaBarang_N_noPO"
style="@style/Widget.Inventory.ListItemTextView"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="2"
android:fontFamily="sans-serif"
tools:text="Terigu (P001)" />
<TextView
android:id="@+id/rv_tanggalPO"
style="@style/Widget.Inventory.ListItemTextView"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1.2"
android:layout_marginHorizontal="@dimen/margin_between_elements"
android:fontFamily="sans-serif-medium"
android:textAlignment="textStart"
tools:text="21/12/2022" />
<TextView
android:id="@+id/rv_jumlahPO"
style="@style/Widget.Inventory.ListItemTextView"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:fontFamily="sans-serif-medium"
android:textAlignment="textEnd"
tools:text="100Kg" />
<TextView
android:id="@+id/rv_hargaPO"
style="@style/Widget.Inventory.ListItemTextView"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1.5"
android:fontFamily="sans-serif-medium"
android:textAlignment="textEnd"
tools:text="110.000.000" />
</LinearLayout>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|

