这是 Reyclerview 的适配器类 我的问题是,当我在 onBindViewHolder 方法中调用 firebase 文档时,由于 addSnapshotListener 的回调,它会调用无限循环,因为当 firebase 添加快照 Litener 并设置为特定文本视图时,我需要立即反映数据。我已经在我的旧项目上工作正常,但现在它不起作用。
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)
}
}
`
这是我的片段调用和列表的重新引用。我确认问题出在适配器类而不是片段类上。调试并打印日志检查三次。
` 类 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)
}
}
}
这是 Reyclerview 的适配器类 我的问题是,当我在 onBindViewHolder 方法中调用 firebase 文档时,由于 addSnapshotListener 的回调,它会调用无限循环,因为当 firebase 添加快照 Litener 并设置为特定文本视图时,我需要立即反映数据。我已经在我的旧项目上工作正常,但现在它不起作用。
首先将侦听器移出 viewHolders,切勿在 Adapters/ViewHolders 中设置 firebase 侦听器或任何类型的服务, ViewHolders/Adapters 应该只负责创建和绑定视图,它们不能知道数据来自何处的数据服务。 试试这个它会解决所有问题