Reputation: 21
I have created the Simple Project for Item to be Listed in one fragment and to do CRUD operation in another Fragment . I have used Live data ,Viewmodel and Sql lite instead to Room.
According to the Definition of Live Data :
"Instead of updating the UI every time the app data changes, your observer can update the UI every time there's a change."
But its not working
I have tried to just check the working live data by implementing delay in DB operations (For Example : When user enters the item details and tries to click the save button , user just returns to Item Listing Fragment and after delay of 3 Sec the item will be updated in the DB. After the updation the listview is not updated.)
Code :
Item Listing Fragment :
class ItemFragment : Fragment()
{
private lateinit var adapter : CustomCursorAdapter
private lateinit var databaseHelper : DatabaseHelper
private lateinit var binding : FragmentItemListBinding
private val itemViewModel : ItemViewModel by viewModels()
override fun onCreateView(inflater : LayoutInflater, container: ViewGroup?, savedInstanceState : Bundle?): View?
{
binding = FragmentItemListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
{
setUpToolBar()
updatedListView()
setClickListeners()
}
private fun setUpToolBar()
{
binding.toolbar.setTitle("Item Listing")
binding.toolbar.setNavigationIcon(R.drawable.baseline_arrow_back_24)
binding.toolbar.setNavigationOnClickListener {
activity?.finish()
}
}
private fun onListItemClick(position: Int)
{
val cursor = adapter.cursor
if(cursor.moveToPosition(position))
{
val item = Item(cursor)
val addOrEditItemFragment = AddOrEditItemFragment().apply {
arguments = Bundle().apply {
putInt("itemId", item.itemId)
}
}
val fragmentContainer = if(configuration.isDeviceInLandScapeMode(resources)) R.id.fragmentB else R.id.fragmentA
navigateToAddOrEditFragment(fragmentContainer ,addOrEditItemFragment)
}
}
private fun navigateToAddOrEditFragment(fragmentContainer : Int, addOrEditItemFragment:Fragment)
{
parentFragmentManager.beginTransaction()
.replace(fragmentContainer, addOrEditItemFragment,"addOrEditItemFragment")
.addToBackStack("AddOrEditItemFragment")
.commit()
}
private fun setClickListeners()
{
binding.listView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
onListItemClick(position)
}
if (configuration.isDeviceInLandScapeMode(resources))
{
binding.add.visibility = View.GONE
}
else
{
binding.add.setOnClickListener()
{
navigateToAddOrEditFragment(R.id.fragmentA,AddOrEditItemFragment())
}
}
}
private fun updatedListView()
{
adapter = CustomCursorAdapter(requireContext(), null)
binding.listView.adapter = adapter
itemViewModel.itemsCursorLiveData.observe(viewLifecycleOwner) { cursor ->
println("Cursor : "+ cursor)
adapter.changeCursor(cursor)
}
itemViewModel.fetchItems()
}
}
Item View Model :
class ItemViewModel(application: Application) : AndroidViewModel(application)
{
private val databaseHelper = DatabaseHelper.getInstance(application)
private val _itemsCursorLiveData = MutableLiveData<Cursor>()
val itemsCursorLiveData: LiveData<Cursor> get() = _itemsCursorLiveData
fun fetchItems()
{
viewModelScope.launch(Dispatchers.IO)
{
val cursor = databaseHelper.getItems()
withContext(Dispatchers.Main)
{
_itemsCursorLiveData.postValue(cursor)
}
}
}
}
Add or Edit Item Fragment :
class AddOrEditItemFragment : Fragment()
{
private lateinit var newItem : Item
private lateinit var imageIntentListener : ActivityResultLauncher<Intent>
private val viewModel : ItemDetailsViewModel by viewModels()
private var binding : FragmentAddoredititemBinding ?= null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View?
{
binding = FragmentAddoredititemBinding.inflate(inflater, container, false)
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
{
super.onViewCreated(view, savedInstanceState)
setupItemDetails()
setupObservers()
handleDeviceBackButton()
setOnButtonClickListeners()
fragmentResultListeners()
}
private fun setupItemDetails()
{
val itemId = arguments?.getInt("itemId") ?: -1
if (itemId != -1)
{
setUpToolBar(true)
viewModel.loadItem(itemId)
}
else {
setUpToolBar(false)
newItem = Item()
}
}
private fun setupObservers()
{
viewModel.item.observe(viewLifecycleOwner) { item ->
newItem = item
binding?.editName?.setText(newItem.name)
binding?.editPrice?.setText(newItem.price)
binding?.editSku?.setText(newItem.sku)
binding?.editdesc?.setText(newItem.desc)
if (newItem.imageResource != Uri.EMPTY)
{
binding?.imageView?.setImageURI(newItem.imageResource)
}
}
}
private fun handleDeviceBackButton()
{
val backPressedCallback = object : OnBackPressedCallback(true)
{
override fun handleOnBackPressed()
{
if((configuration.isDeviceInLandScapeMode(resources) && newItem.itemId != -1) || !configuration.isDeviceInLandScapeMode(resources))
{
showAlertDialogBox()
}
else
{
activity?.finish()
}
}
}
activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner, backPressedCallback)
}
private fun setNavigationIcon()
{
binding?.toolbar?.setNavigationIcon(R.drawable.baseline_arrow_back_24)
binding?.toolbar?.setNavigationOnClickListener {
showAlertDialogBox()
}
}
private fun setUpToolBar(isEdit : Boolean)
{
if(isEdit)
{
binding?.toolbar?.title = "Edit Item"
binding?.delete?.visibility = View.VISIBLE
setNavigationIcon()
}
else
{
binding?.toolbar?.title = "Add Item"
if(!configuration.isDeviceInLandScapeMode(resources)) setNavigationIcon()
}
}
private fun showAlertDialogBox()
{
val builder = AlertDialog.Builder(context)
builder.setMessage("Do you want to exit ?")
builder.setCancelable(false)
builder.setPositiveButton("Yes") { _, _ -> parentFragmentManager.popBackStack("AddOrEditItemFragment", FragmentManager.POP_BACK_STACK_INCLUSIVE) }
builder.setNegativeButton("No") { dialog, _ -> dialog.cancel() }
val alertDialog = builder.create()
alertDialog.show()
}
private fun saveItem()
{
if(fieldEmptyOrNot())
{
newItem.apply {
name = binding?.editName?.text.toString()
price = binding?.editPrice?.text.toString()
sku = binding?.editSku?.text.toString()
desc = binding?.editdesc?.text.toString()
}
println("BEFORE SAVING : "+newItem)
viewModel.saveItem(newItem)
parentFragmentManager.popBackStack()
}
}
private fun setOnButtonClickListeners()
{
binding?.imageView?.setOnClickListener{
chooseImageGallery()
}
binding?.save?.setOnClickListener {
saveItem()
}
binding?.delete?.setOnClickListener{
deleteItem()
}
}
private fun fieldEmptyOrNot():Boolean
{
if (binding?.editName?.text.toString().isBlank())
{
binding?.editName?.error = "Please enter a name"
return false
}
if (binding?.editPrice?.text.toString().isBlank())
{
binding?.editPrice?.error = "Please enter a price"
return false
}
if(binding?.editSku?.text.toString().isBlank())
{
binding?.editSku?.error = "Please enter a SKU"
return false
}
return true
}
private fun chooseImageGallery()
{
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "image/*"
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
imageIntentListener.launch(intent)
}
private fun fragmentResultListeners()
{
imageIntentListener = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK)
{
newItem.imageResource = result.data?.data as Uri
activity?.contentResolver?.takePersistableUriPermission(newItem.imageResource, Intent.FLAG_GRANT_READ_URI_PERMISSION)
binding?.imageView?.setImageURI(newItem.imageResource)
}
}
}
private fun deleteItem()
{
newItem.let { viewModel.deleteItem(it.itemId) }
parentFragmentManager.popBackStack()
}
}
Item View Details View Model :
ItemDetailsViewModel(application : Application) : AndroidViewModel(application)
{
private val _item = MutableLiveData<Item>()
val item: LiveData<Item> get() = _item
private val databaseHelper = DatabaseHelper.getInstance(application)
fun loadItem(itemId: Int)
{
viewModelScope.launch(Dispatchers.IO)
{
val cursor = databaseHelper.getItemByID(itemId)
if (cursor.moveToFirst())
{
val loadedItem = Item(cursor)
withContext(Dispatchers.Main) {
_item.value = loadedItem
}
}
}
}
fun saveItem(item: Item)
{
CoroutineScope(Dispatchers.IO).launch()
{
println("Thread ${Thread.currentThread()}")
delay(3000L)
println("Running after delay")
if (item.itemId != -1)
{
databaseHelper.updateItem(
item.itemId, item.name, item.price, item.sku,
item.imageResource, item.desc
)
}
else
{
databaseHelper.addItems(
item.name, item.price, item.sku,
item.imageResource, item.desc
)
}
}
}
override fun onCleared() {
super.onCleared()
println("ItemDetailViewModel ViewModel destroyed")
}
fun deleteItem(itemId: Int)
{
viewModelScope.launch(Dispatchers.IO) {
databaseHelper.deleteItemByID(itemId)
}
}
}
Kindly go through the code and Help me to resolve this
Upvotes: 0
Views: 25