Reputation: 1
I am trying to teach myself coding using kotlin. I am using a navigation graph to navigate through various fragments using a mainactivity. THe home fragment displays in a recyclerview a list of towns, from a firebase realtime database, with a searchquery on the town name. The user can select a single item on the recyclerview and go to the update fragment where one or more fields can be updated. There is a button in the update fragment that allows the user to update the item. After validation the user is returned to the home fragment. Because of the eventlistener on firebase the updated item is correctly displayed in the recyclerview. If the user returns without updating the data remains the same.If another user updates an item that is displayed in this users list it is immediately updated. There are 4 options. No search criteria, return from update without updating item. Works ok. No search criteria, return from update fragment after updating item. Works ok. With search criteria, return from update fragment without updating. Works ok. (list shown is meets the search criteria) With search criteria, return from update fragment after updating item. Not working - the previous search criteria is not being reapplied and the full list is being displayed. I am not using diffutil, but when I did I had the same problem. town data class
data class Towns(
val townCountryCode: String? = null,
val townCode: String? = null,
val townName: String? = null,
val townLat: String? = null,
val townLng: String? = null,
val townEmail: String? = null,
val townFranId: String? = null
)
class HometownFragment : Fragment() {
private var bindingH: FragmentHometownBinding? = null
private val binding get() = bindingH!!
private lateinit var townsList: ArrayList<Towns>
private lateinit var firebaseRef: DatabaseReference
private lateinit var rvTownsAdapter: RvTownsAdapter
private var valueEventListener: ValueEventListener? = null
private var currentSearchQuery: String? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
bindingH = FragmentHometownBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Restore search query from savedInstanceState (Bundle)
currentSearchQuery = savedInstanceState?.getString("SEARCH_QUERY")
Log.d("hometown", "currentSearchQuery restore $currentSearchQuery ")
// Handle back press to sign out
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
SignOutUtils.signOutHandler(this@HometownFragment)
}
})
binding.btnAddT.setOnClickListener {
findNavController().navigate(R.id.action_hometownFragment_to_addtownFragment)
}
// Shared preferences to get country and set up Firebase reference
val sharePreference = activity?.getSharedPreferences("MY_PRE", Context.MODE_PRIVATE)
val userCountry = sharePreference?.getString("USERCOUNTRY", "").toString()
firebaseRef = FirebaseDatabase.getInstance().getReference("towns").child(userCountry)
townsList = arrayListOf()
rvTownsAdapter = RvTownsAdapter(townsList)
// Set up RecyclerView
binding.rvTowns.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(this.context)
adapter = rvTownsAdapter
}
// Fetch data from Firebase and update the list
fetchData()
// Handle search query changes
binding.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
binding.searchView.clearFocus()
Log.d("hometown", "in onQueryTextSubmit ")
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
Log.d("hometown", "currentSearchQuery text change before newText $currentSearchQuery ")
currentSearchQuery = newText
Log.d("hometown", "currentSearchQuery text change after newText $currentSearchQuery ")
applySearchQuery() // Reapply filter immediately when query changes
return true
}
})
// If there was a saved search query, apply it
currentSearchQuery?.let {
binding.searchView.setQuery(it, false)
applySearchQuery() // Apply the filter
}
}
override fun onPause() {
super.onPause()
// Save the current search query
currentSearchQuery = binding.searchView.query.toString()
Log.d("hometown", "currentSearchQuery on pause $currentSearchQuery ")
}
override fun onResume() {
super.onResume()
// Restore the search query when coming back to the fragment
currentSearchQuery?.let {
binding.searchView.setQuery(it, false)
}
Log.d("hometown", "currentSearchQuery on resume $currentSearchQuery ")
}
private fun fetchData() {
valueEventListener?.let {
firebaseRef.removeEventListener(it)
}
valueEventListener = firebaseRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
townsList.clear()
if (snapshot.exists()) {
for (contactSnap in snapshot.children) {
val towns = contactSnap.getValue(Towns::class.java)
townsList.add(towns!!)
}
}
// After data is fetched, apply the search query if there is one
applySearchQuery()
// Ensure that the search query listener is still set
binding.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
binding.searchView.clearFocus()
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
Log.d("hometown", "currentSearchQuery text change before 2nd newText $currentSearchQuery ")
currentSearchQuery = newText
Log.d("hometown", "currentSearchQuery text change after 2nd newText $currentSearchQuery ")
applySearchQuery() // Reapply filter immediately when query changes
return true
}
})
}
override fun onCancelled(error: DatabaseError) {
Toast.makeText(context, "Error fetching data: $error", Toast.LENGTH_SHORT).show()
}
})
}
private fun applySearchQuery() {
// Always apply search query when the data is updated
val filteredList = if (currentSearchQuery.isNullOrEmpty()) {
townsList // Show all towns if there's no search query
} else {
townsList.filter {
it.townName!!.lowercase().contains(currentSearchQuery!!.lowercase())
}
}
rvTownsAdapter.updateList(ArrayList(filteredList))
if (filteredList.isEmpty() && townsList.isNotEmpty()) {
ToastUtils.showToast(requireContext(), R.string.no_data_available)
}
}
}
class RvTownsAdapter(private var townsList: ArrayList<Towns>) :
RecyclerView.Adapter<RvTownsAdapter.ViewHolder>() {
class ViewHolder(val binding: RvTownsItemBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(RvTownsItemBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun getItemCount(): Int {
return townsList.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val currentItem = townsList[position]
val context = holder.itemView.context
holder.binding.apply {
tvTownCodeItem.text = currentItem.townCode
tvTownNameItem.text = currentItem.townName
tvTownLatItem.text = currentItem.townLat
tvTownLngItem.text = currentItem.townLng
tvTownEmailItem.text = currentItem.townEmail
tvTownFranIdItem.text = currentItem.townFranId
rvTownContainer.setOnClickListener {
navigateToUpdateTownFragment(currentItem, holder)
}
rvTownContainer.setOnLongClickListener {
handleLongClick(context, currentItem, holder)
true
}
btnAddS.setOnClickListener {
Log.d("Navigate", "townCode: $currentItem.townCode, townName: $currentItem.townName")
navigateToStreetFragment(currentItem, holder)
}
}
}
fun updateList(newList: ArrayList<Towns>) {
townsList = newList
notifyDataSetChanged()
}
private fun navigateToUpdateTownFragment(currentItem: Towns, holder: ViewHolder) {
val actionUpdate = HometownFragmentDirections.actionHometownFragmentToUpdatetownFragment(
currentItem.townCountryCode?: "",
currentItem.townCode?: "",
currentItem.townName?: "",
currentItem.townLat?: "",
currentItem.townLng?: "",
currentItem.townEmail?: "",
currentItem.townFranId?: ""
)
findNavController(holder.itemView).navigate(actionUpdate)
}
private fun navigateToStreetFragment(currentItem: Towns, holder: RvTownsAdapter.ViewHolder) {
val action = HometownFragmentDirections.actionHometownFragmentToHomestreetFragment(
currentItem.townCode?: "",
currentItem.townName?: ""
)
Log.d("NavigateToStreet", "townCode: $currentItem.townCode, townName: $currentItem.townName")
findNavController(holder.itemView).navigate(action)
}
private fun showMessageShort(context: Context, message: String) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
}
I tried storing the currentSearchQuery in SEARCH_QUERY and reapplying it on returning from the update fragment but that is not working. After many different attempts I added log.d to monitor what happens. With my current code, which I recognize is not efficient, When using the search criteria est
Upvotes: 0
Views: 20