This is adapterclass of Reyclerview My question is when I am calling firebase document in onBindViewHolder method it callining infinte loop due to call back of addSnapshotListener becuse I need to data reflect instantly when firebase addsnapshot litener and set into specific text view. where I am already set on my old project working fine but now it's not working.
package com.wedoapps.cricketLiveLine.adapter
import android.content.res.ColorStateListimport android.text.TextUtilsimport android.util.Logimport android.view.LayoutInflaterimport android.view.Viewimport android.view.ViewGroupimport androidx.core.content.ContextCompatimport androidx.recyclerview.widget.RecyclerViewimport com.bumptech.glide.Glideimport com.google.firebase.firestore.FirebaseFirestoreimport com.google.firebase.firestore.ListenerRegistrationimport com.google.firebase.storage.FirebaseStorageimport com.wedoapps.cricketLiveLine.Rimport com.wedoapps.cricketLiveLine.databinding.LayoutHomeItemBindingimport com.wedoapps.cricketLiveLine.model.HomeMatchimport com.wedoapps.cricketLiveLine.model.Scoreimport com.wedoapps.cricketLiveLine.utils.Constantsimport com.wedoapps.cricketLiveLine.utils.Constants.TAGimport com.wedoapps.cricketLiveLine.utils.ShowLogToastimport java.text.SimpleDateFormatimport java.util.Calendarimport java.util.Locale
class HomeCardAdapter(private val listener: SetOnClick,private val homeMatchArrayList: ArrayList<HomeMatch>) : RecyclerView.Adapter<HomeCardAdapter.HomeCardViewHolder>() {
private var binderCounter = 0
private var createCounter = 0
val storageRef = FirebaseStorage.getInstance().reference
var fireStore = FirebaseFirestore.getInstance()
private var fireStoreRef = fireStore.collection(Constants.collectionPathMatchList)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeCardViewHolder {
ShowLogToast.showLog("Counter for OnCreateViewHolder-->", createCounter.toString())
createCounter++
val binding =
LayoutHomeItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return HomeCardViewHolder(binding)
}
override fun onBindViewHolder(holder: HomeCardViewHolder, position: Int) {
val currentItem = homeMatchArrayList[position]
ShowLogToast.showLog("Counter for onBindViewHolder-->", binderCounter.toString())
binderCounter++
with(holder) {
binding.tvMatch.text = currentItem.matchDetail?.trim()
if (currentItem.matchDate == null) {
binding.tvTime.text = currentItem.currentDate?.trim()
} else {
fun Long?.getDateMatchList(): String {
val sdf = SimpleDateFormat("dd MMMM yyyy, h:mm a", Locale.ENGLISH)
val cal = Calendar.getInstance(Locale.ENGLISH)
cal.timeInMillis = this?.times(1000L) ?: 0L
return sdf.format(cal.time)
}
binding.tvTime.text = currentItem.matchDate.getDateMatchList()
}
when (currentItem.matchStatus) {
Constants.statusUpcoming -> {
Log.d(TAG, "UP: $currentItem")
binding.tvOver.visibility = View.GONE
binding.tvFInn.visibility = View.GONE
binding.tvSecondFInn.visibility = View.GONE
binding.tvSecondOver.visibility = View.GONE
binding.tvFirstTeam.visibility = View.VISIBLE
binding.tvSecondTeam.visibility = View.VISIBLE
binding.tvRate1.visibility = View.VISIBLE
binding.tvRate2.visibility = View.VISIBLE
binding.tvFavTeam.visibility = View.VISIBLE
binding.tvDayStatus.visibility = View.VISIBLE
binding.relativeBottom.visibility = View.VISIBLE
binding.btmDiv.visibility = View.VISIBLE
binding.tvDayStatus.text = currentItem.venue?.trim()
binding.tvMatchStatus.text = Constants.statusUpcoming
binding.tvMatchStatus.backgroundTintList =
ColorStateList.valueOf(itemView.context.getColor(R.color.yellow_orange))
binding.tvFirstTeam.text =
if (currentItem.fullNameTeam1.isNullOrEmpty()) currentItem.team1?.trim() else currentItem.fullNameTeam1!!.trim()
binding.tvSecondTeam.text =
if (currentItem.fullNameTeam2.isNullOrEmpty()) currentItem.team2?.trim() else currentItem.fullNameTeam2!!.trim()
binding.tvFirstTeam.maxLines = 3
binding.tvSecondTeam.maxLines = 3
binding.tvFirstTeam.setLineSpacing(0.8f, 0.8f)
binding.tvSecondTeam.setLineSpacing(0.8f, 0.8f)
}
Constants.statusCompleted -> {
binding.tvFInn.maxLines = 1
binding.tvSecondFInn.maxLines = 1
binding.tvFInn.visibility = View.VISIBLE
binding.tvOver.visibility = View.VISIBLE
binding.tvSecondFInn.visibility = View.VISIBLE
binding.tvSecondOver.visibility = View.VISIBLE
binding.tvFirstTeam.visibility = View.VISIBLE
binding.tvSecondTeam.visibility = View.VISIBLE
binding.tvRate1.visibility = View.GONE
binding.tvRate2.visibility = View.GONE
binding.tvFavTeam.visibility = View.GONE
binding.btmDiv.visibility = View.INVISIBLE
binding.tvDayStatus.visibility = View.GONE
binding.tvMatchStatus.text = buildString {
append("Finished")
}
binding.relativeBottom.visibility = View.GONE
binding.tvMatchStatus.backgroundTintList =
ColorStateList.valueOf(itemView.context.getColor(R.color.green))
binding.tvFirstTeam.text =
if (currentItem.codeTeam1.isNullOrEmpty()) currentItem.team1?.trim() else currentItem.codeTeam1!!.trim()
binding.tvSecondTeam.text =
if (currentItem.codeTeam2.isNullOrEmpty()) currentItem.team2?.trim() else currentItem.codeTeam2!!.trim()
}
Constants.statusLive -> {
binding.tvFInn.maxLines = 1
binding.tvSecondFInn.maxLines = 1
binding.tvFInn.visibility = View.VISIBLE
binding.tvOver.visibility = View.VISIBLE
binding.tvSecondFInn.visibility = View.VISIBLE
binding.tvSecondOver.visibility = View.VISIBLE
binding.tvFirstTeam.visibility = View.VISIBLE
binding.tvSecondTeam.visibility = View.VISIBLE
binding.tvRate1.visibility = View.VISIBLE
binding.tvRate2.visibility = View.VISIBLE
binding.tvFavTeam.visibility = View.VISIBLE
binding.tvDayStatus.visibility = View.VISIBLE
binding.relativeBottom.visibility = View.VISIBLE
binding.btmDiv.visibility = View.VISIBLE
binding.tvMatchStatus.text = buildString {
append("◉ LIVE")
}
binding.tvMatchStatus.backgroundTintList =
ColorStateList.valueOf(itemView.context.getColor(R.color.colorRed))
binding.tvFirstTeam.text =
if (currentItem.codeTeam1.isNullOrEmpty()) currentItem.team1?.trim() else currentItem.codeTeam1!!.trim()
binding.tvSecondTeam.text =
if (currentItem.codeTeam2.isNullOrEmpty()) currentItem.team2?.trim() else currentItem.codeTeam2!!.trim()
}
else -> {
binding.tvMatchStatus.text = currentItem.matchStatus?.trim()
}
}
val imgPlayerDrawableDefault =
ContextCompat.getDrawable(itemView.context, R.drawable.imgpsh_fullsize_anim)
val fileReference1 =
storageRef.child(Constants.flagIconPath + currentItem.team1?.trim { it <= ' ' } + ".png")
fileReference1.metadata.addOnSuccessListener {
if (it.name != null && it.sizeBytes > 0) {
fileReference1.downloadUrl.addOnSuccessListener { uri ->
Log.d(TAG, "TeamH1URL: $uri")
Glide.with(itemView.context)
.load(uri)
.centerCrop()
.placeholder(R.drawable.imgpsh_fullsize_anim)
.into(binding.ivFirstTeam)
}.addOnFailureListener {
binding.ivFirstTeam.setImageDrawable(
imgPlayerDrawableDefault
)
}
}
}.addOnFailureListener {
ShowLogToast.showLog("Storage Exception Team1 flag-->", it.localizedMessage)
}
val fileReference2 =
storageRef.child(Constants.flagIconPath + currentItem.team2?.trim { it <= ' ' } + ".png")
fileReference2.metadata.addOnSuccessListener {
if (it.name != null && it.sizeBytes > 0) {
fileReference2.downloadUrl.addOnSuccessListener { uri ->
Log.d(TAG, "TeamH2URL: $uri")
Glide.with(itemView.context)
.load(uri)
.centerCrop()
.placeholder(R.drawable.imgpsh_fullsize_anim)
.into(binding.ivSecondTeam)
}.addOnFailureListener {
binding.ivSecondTeam.setImageDrawable(
imgPlayerDrawableDefault
)
}
}
}.addOnFailureListener {
ShowLogToast.showLog("Storage Exception Team2 flag-->", it.localizedMessage)
}
fireStoreRef.document(currentItem.id!!).collection(Constants.lastBallInfoKey)
.document(Constants.lbdKey)
.addSnapshotListener { value, error ->
if (error != null) {
Log.w(TAG, "Listen Failed", error)
return@addSnapshotListener
}
if (value != null) {
val data = value.data
if (data != null) {
val ballByBall: String = data[Constants.ballByBallKey] as String
Log.d(TAG, "LBD: $data")
binding.txtBallByBallUpdate.text = ballByBall
when {
ballByBall.contentEquals("0 Run") -> {
binding.txtBallByBallUpdate.text = "0"
}
ballByBall.contentEquals("1 Run") -> {
binding.txtBallByBallUpdate.text = "1"
}
ballByBall.contentEquals("2 Run") -> {
binding.txtBallByBallUpdate.text = "2"
}
ballByBall.contentEquals("3 Run") -> {
binding.txtBallByBallUpdate.text = "3"
}
ballByBall.contentEquals("It's 6") -> {
binding.txtBallByBallUpdate.text = "6-6-6"
}
ballByBall.contentEquals("It's 4") -> {
binding.txtBallByBallUpdate.text = "4-4-4"
}
ballByBall.contentEquals("WD + 1") -> {
binding.txtBallByBallUpdate.text = buildString {
append("WD1")
}
}
ballByBall.contentEquals("WD + 2") -> {
binding.txtBallByBallUpdate.text = buildString {
append("WD2")
}
}
}
}
} else {
Log.d(TAG, "No Data")
}
}
fireStoreRef.document(currentItem.id!!).collection(Constants.matchRateKey)
.document(Constants.matchKey)
.addSnapshotListener { value, error ->
if (error != null) {
Log.w(TAG, "Listen Failed", error)
return@addSnapshotListener
}
if (value != null) {
val data = value.data
if (data != null) {
val it = data.toString()
val value1 = it.substring(1, it.length - 1)
val keyValuePair = value1.split(",")
val map = hashMapOf<String, String>()
keyValuePair.forEach { text ->
val entry = text.split("=")
map[entry[0].trim()] = entry[1].trim()
}
val favTeam = map[Constants.favTeamKey]?.trim()
val rate1 = map[Constants.rate1Key]?.trim()
val rate2 = map[Constants.rate2Key]?.trim()
when (currentItem.matchStatus) {
Constants.statusUpcoming -> {
if (TextUtils.isEmpty(favTeam)) {
binding.tvFavTeam.visibility = View.GONE
} else {
binding.tvFavTeam.visibility = View.VISIBLE
binding.tvFavTeam.text = favTeam
}
if (TextUtils.isEmpty(rate1)) {
binding.tvRate1.visibility = View.GONE
} else {
binding.tvRate1.visibility = View.VISIBLE
binding.tvRate1.text = rate1
}
if (TextUtils.isEmpty(rate2)) {
binding.tvRate2.visibility = View.GONE
} else {
binding.tvRate2.visibility = View.VISIBLE
binding.tvRate2.text = rate2
}
}
Constants.statusLive -> {
if (TextUtils.isEmpty(favTeam)) {
binding.tvFavTeam.visibility = View.GONE
} else {
binding.tvFavTeam.visibility = View.VISIBLE
binding.tvFavTeam.text = favTeam
}
if (TextUtils.isEmpty(rate1)) {
binding.tvRate1.visibility = View.GONE
} else {
binding.tvRate1.visibility = View.VISIBLE
binding.tvRate1.text = rate1
}
if (TextUtils.isEmpty(rate2)) {
binding.tvRate2.visibility = View.GONE
} else {
binding.tvRate2.visibility = View.VISIBLE
binding.tvRate2.text = rate2
}
}
Constants.statusCompleted -> {
binding.tvFavTeam.visibility = View.GONE
binding.tvRate1.visibility = View.GONE
binding.tvRate2.visibility = View.GONE
}
}
Log.d(TAG, "mr: $data")
} else {
binding.tvFavTeam.visibility = View.GONE
binding.tvRate2.visibility = View.GONE
binding.tvRate1.visibility = View.GONE
}
} else {
Log.d(TAG, "No Data")
binding.tvFavTeam.visibility = View.GONE
binding.tvRate2.visibility = View.GONE
binding.tvRate1.visibility = View.GONE
}
}
fireStoreRef.document(currentItem.id!!).collection(Constants.collectionPathLiveMatch)
.document(Constants.runRateInfoKey)
.addSnapshotListener { value, error ->
if (error != null) {
Log.w(TAG, "Listen Failed", error)
return@addSnapshotListener
}
if (value != null) {
val data = value.data
if (data != null) {
val it = data.toString()
val value1 = it.substring(1, it.length - 1)
val keyValuePair = value1.split(",")
val map = hashMapOf<String, String>()
keyValuePair.forEach { text ->
val entry = text.split("=")
map[entry[0].trim()] = entry[1].trim()
}
binding.tvDayStatus.text = if (map[Constants.otherInfoKey]?.trim()
.isNullOrEmpty() || map[Constants.otherInfoKey].equals("") || map[Constants.otherInfoKey].equals(
"-"
)
) if (currentItem.venue.isNullOrEmpty()) "" else "${currentItem.venue}" else map[Constants.otherInfoKey]?.trim()
Log.d(TAG, "getRunRate: $data")
}
} else {
Log.d(TAG, "No Data")
}
}
fireStoreRef.document(currentItem.id!!.toString())
.collection(Constants.collectionPathLiveMatch).document(Constants.scoreTeam1Key)
.addSnapshotListener { value, error ->
if (error != null) {
Log.w(TAG, "Listen Failed", error)
return@addSnapshotListener
}
if (value != null) {
val allTeam1 = value.toObject(Score::class.java)
binding.tvFInn.text = value.get(Constants.scoreKey).toString().trim()
binding.tvOver.text = buildString {
append("( ")
append(value.get(Constants.overKey).toString())
append(" )")
}
//val allTeam1 = ArrayList<Score>()
//allTeam1?.add(allTeam1)
Log.d(TAG, "team1 HomeCard: $allTeam1")
} else {
Log.d(TAG, "No Data")
}
}
fireStoreRef.document(currentItem.id!!.toString())
.collection(Constants.collectionPathLiveMatch).document(Constants.scoreTeam2Key)
.addSnapshotListener { value, error ->
if (error != null) {
Log.w(TAG, "Listen Failed", error)
return@addSnapshotListener
}
if (value != null) {
val allTeam2 = value.toObject(Score::class.java)
binding.tvSecondFInn.text = value.get(Constants.scoreKey).toString().trim()
binding.tvSecondOver.text = buildString {
append("( ")
append(value.get(Constants.overKey).toString())
append(" )")
}
Log.d(TAG, "team2: $allTeam2")
} else {
Log.d(TAG, "No Data")
}
}
}
}
override fun getItemCount(): Int {
ShowLogToast.showLog("HomeCardAdapter size-->", homeMatchArrayList.size.toString())
return homeMatchArrayList.size
// return differ.currentList.size
}
override fun onViewRecycled(holder: HomeCardViewHolder) {
super.onViewRecycled(holder)
val position: Int = holder.absoluteAdapterPosition
}
inner class HomeCardViewHolder(var binding: LayoutHomeItemBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.root.setOnClickListener {
val position = absoluteAdapterPosition
if (position != RecyclerView.NO_POSITION) {
val item = homeMatchArrayList[position]
listener.onClick(item)
}
}
}
}
/* private val differCallback = object : DiffUtil.ItemCallback<HomeMatch>() {
override fun areItemsTheSame(oldItem: HomeMatch, newItem: HomeMatch) =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: HomeMatch, newItem: HomeMatch) =
oldItem.id == newItem.id
}
val differ = AsyncListDiffer(this, differCallback)*/
interface SetOnClick {
fun onClick(match: HomeMatch)
}
}
`
This is my fragment calss where to Calling and refeleing of list. I am verify that the issue is on adapter class not fragment calss. debug and also print the log to check thrice.
` class HomeMatchListFragment : BaseFragment(), View.OnClickListener, HomeCardAdapter.SetOnClick { private val tagHomeMatch: String = "HomeMatchListFragment" private lateinit var mBinding: FragmentHomeMatchListBinding
private var mContext: Context? = null
private var dialog: Dialog? = null
private lateinit var viewModel: CricketGuruViewModel
private val fireStore = FirebaseFirestore.getInstance()
private val firestorage = FirebaseStorage.getInstance()
private val storageRef = firestorage.reference
private var permissionChecker: PermissionChecker? = null
private lateinit var matchScoreListAdapter: HomeCardAdapter
private var team1 = ArrayList<String>()
private var team2 = ArrayList<String>()
private var homeMatchArrayList: ArrayList<HomeMatch> = ArrayList()
override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context
permissionChecker = PermissionChecker(mContext!!)
}
override fun getInflateResource(): Int {
return R.layout.fragment_home_match_list
}
override fun displayMessage(message: String) {
mBinding.root.snack(message)
}
override fun initView() {
mBinding = getBinding()
viewModel = (activity as MainActivity).viewModel
setRecyclerView()
}
override fun postInit() {
}
override fun handleListener() {
}
override fun initProgressBar() {
dialog = Dialog(requireContext())
dialog!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog!!.setContentView(R.layout.dialog_progress)
dialog!!.setCancelable(false)
}
override fun showLoadingIndicator(isShow: Boolean) {
dialog!!.isVisible(isShow, dialog)
}
private fun setRecyclerView() {
matchScoreListAdapter = HomeCardAdapter(this,homeMatchArrayList)
val mLayoutManager: RecyclerView.LayoutManager = LinearLayoutManager(context)
mBinding.recyclerViewHomeLiveMatchList.layoutManager = mLayoutManager
mBinding.recyclerViewHomeLiveMatchList.itemAnimator = DefaultItemAnimator()
mBinding.recyclerViewHomeLiveMatchList.setHasFixedSize(true)
mBinding.recyclerViewHomeLiveMatchList.adapter = matchScoreListAdapter
// Set the RecyclerViewPool to the RecyclerViews
mBinding.recyclerViewHomeLiveMatchList.recycledViewPool.clear()
showShimmerViewVisibility(true)
var count =0
viewModel.getAllMatch().observe(this) {
// matchScoreListAdapter.differ.submitList(it?.toMutableList())
count++
ShowLogToast.showLog(tagHomeMatch, "Count is-->$count")
homeMatchArrayList.clear()
homeMatchArrayList.addAll(it)
if (it.size > 0) {
// matchScoreListAdapter.notifyItemRangeInserted(0,homeMatchArrayList.size)
setRecyclerViewVisibility(true)
} else {
setRecyclerViewVisibility(false)
}
showShimmerViewVisibility(false)
ShowLogToast.showLog("Test Home Array Size", it.size.toString())
}
if(homeMatchArrayList.size >0){
matchScoreListAdapter.notifyItemRangeInserted(0,homeMatchArrayList.size)
}
}
private fun showShimmerViewVisibility(b: Boolean) {
if (b) {
mBinding.recyclerViewHomeLiveMatchList.showShimmerAdapter()
} else {
mBinding.recyclerViewHomeLiveMatchList.hideShimmerAdapter()
}
}
private fun setRecyclerViewVisibility(b: Boolean) {
if (b) {
mBinding.txtNoDataHomeLiveMatchList.visibility = View.GONE
mBinding.recyclerViewHomeLiveMatchList.visibility = View.VISIBLE
} else {
mBinding.recyclerViewHomeLiveMatchList.visibility = View.GONE
mBinding.txtNoDataHomeLiveMatchList.visibility = View.VISIBLE
}
}
override fun onClick(v: View?) {
when (v!!.id) {
/* R.id.linearHomeAdsLiveMatch->{
CommonUtils.gotoAdsContact(mContext!!)
}*/
}
}
override fun onClick(match: HomeMatch) {
val intentHomeFragment = Intent(requireActivity(), ViewPagerActivity::class.java)
intentHomeFragment.putExtra("match", match)
startActivity(intentHomeFragment)
}
override fun onResume() {
super.onResume()
if (homeMatchArrayList.size > 0) {
matchScoreListAdapter.notifyItemRangeChanged(0, homeMatchArrayList.size)
}
}
}
This is adapterclass of Reyclerview My question is when I am calling firebase document in onBindViewHolder method it callining infinte loop due to call back of addSnapshotListener becuse I need to data reflect instantly when firebase addsnapshot litener and set into specific text view. where I am already set on my old project working fine but now it's not working.
First of all move your listeners out of viewHolders, never set firebase listener or any kind of service in Adapters/ViewHolders, ViewHolders/Adapters should be responsible for only creating and binding views they must not be aware of data service from where data is coming. try this it will fix everything