I have an infinite loop in my code. Until I completely shut down the program, it is continuously writing data to my Firebase database... I am in real need, please help. I want my function MemberAdding execute just one time after button is clicked...
override fun MemberAdding(accountKey: String) {
DBdatabaseName!!.reference!!.child(user!!.currentUser!!.uid).child("Group-List").addValueEventListener(object : ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
groupKey = intent.getStringExtra("GroupKey")
val flag = dataSnapshot.child("Groups").child(groupKey).child("members").value.toString()
val newpath = "member" + flag
DBdatabaseName!!.reference!!.child(user!!.currentUser!!.uid).child("Group-List").child("Groups").child(groupKey).child(newpath).setValue(accountKey)
}
override fun onCancelled(databaseError: DatabaseError) {
}
})
DBdatabaseName!!.reference!!.child(user!!.currentUser!!.uid).child("Group-List").addValueEventListener(object : ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
val groupKey = intent.getStringExtra("GroupKey")
var flag = dataSnapshot.child("Groups").child(groupKey).child("members").value.toString()
val flag2 = flag.toInt()
val flag3 = flag2 + 1
DBdatabaseName!!.reference!!.child(user!!.currentUser!!.uid).child("Group-List").child("Groups").child(groupKey).child("members").setValue(flag3)
}
override fun onCancelled(databaseError: DatabaseError) {
}
})
refresh()
}
private fun refresh() {
groupKey = intent.getStringExtra("GroupKey")
val intent = Intent(this#GroupMemberAddingActivity, GroupMemberAddingActivity::class.java)
intent.putExtra("GroupKey", groupKey)
startActivity(intent)
}
}
at below i will also send the Adapter class that i am using
class GroupMemberAddingItemAdapter (context: Context, friendItemList: MutableList<FriendItem>) : BaseAdapter() {
private class ListRowHolder (row: View? ) {
val label: TextView = row!!.findViewById(R.id.friendName_textview) as TextView
val addMember: Button? = row!!.findViewById(R.id.addMember_button) as Button
}
private val mInflater: LayoutInflater = LayoutInflater.from(context)
private var itemList = friendItemList
private var mView: View? = null
private var rowListener: GroupMemberAddingRowListener = context as GroupMemberAddingRowListener
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val friendName: String = itemList.get(position).friendName as String
val friendLastName: String = itemList.get(position).friendLastName as String
val friendemail: String = itemList.get(position).accountKey as String
val vh: ListRowHolder
if (convertView == null) {
mView = mInflater.inflate(R.layout.groupmemberadding_row, parent, false)
vh = ListRowHolder(mView)
mView!!.tag = vh
} else {
mView = convertView
vh = mView!!.tag as ListRowHolder
}
vh.label.text = "$friendName $friendLastName"
vh.addMember!!.setOnClickListener{rowListener.MemberAdding(friendemail)}
return mView!!
}
override fun getItem(index: Int): Any {
return itemList[index]
}
override fun getItemId(index: Int): Long {
return index.toLong()
}
override fun getCount(): Int {
return itemList.size
}
}
my initial row listener is.....
interface GroupMemberAddingRowListener {
fun MemberAdding (accountKey:String)
}
If you need any more code parts just tell me.... Thanks for helping
First, you had to debug and look where it happens, anyway I think it is inside onDataChanged implementation
Since you fire:
DBdatabaseName!!.reference!!.child(user!!.currentUser!!.uid).child("Group-List").child("Groups").child(groupKey).child("members").setValue(flag3)
setValue will happen to fire onDataChanged again.
I found the problem
DBdatabaseName!!.reference!!.child(user!!.currentUser!!.uid).child("Group-List").addValueEventListener(object : ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
Line makes onDataChange is called back every time data changes...(culprit is addValueEventListener) so i changed it to
BdatabaseName!!.reference!!.child(user!!.currentUser!!.uid).child("Group-List").addListenerForSingleValueEvent(object : ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
addListenerForSingleValueEvent calls the onDataChange only once than destroys the all callbacks
Related
I am fetching JSON data from API and passing it in recycler view but if I want to fetch new data and display it in recycler view then I have to clear the list and then add new data in that list and notify the adapter that the data is changed but it is not updated what should I do?
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var recipeViewModel: RecipeViewModel
private lateinit var mainBinding: ActivityMainBinding
private lateinit var recipeAdapter: RecipeAdapter
private lateinit var recipeItemList: ArrayList<Hit>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(mainBinding.root)
recipeViewModel =
ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory
.getInstance(application)
)[RecipeViewModel::class.java]
recipeItemList = arrayListOf()
mainBinding.recyclerView.layoutManager = LinearLayoutManager(this)
mainBinding.recyclerView.hasFixedSize()
recipeAdapter = RecipeAdapter(this)
mainBinding.recyclerView.adapter = recipeAdapter
recipeViewModel.recipeLiveData.observe(this, Observer { recipeItems ->
recipeItemList.addAll(recipeItems.hits)
recipeAdapter.updateRecipes(recipeItemList)
Log.d("RESPONSE", recipeItems.toString())
Log.d("List size", recipeAdapter.itemCount.toString())
})
searchRecipeName()
}
private fun searchRecipeName() {
mainBinding.searchRecipeFabBtn.setOnClickListener {
val view = layoutInflater.inflate(R.layout.recipe_search_layout, null)
val searchRecipeET = view.findViewById<EditText>(R.id.searchRecipeET)
val searchRecipeBtn = view.findViewById<Button>(R.id.searchRecipeBtn)
val bottomSheetDialog = BottomSheetDialog(this)
bottomSheetDialog.apply {
this.setContentView(view)
this.show()
}
searchRecipeBtn.setOnClickListener {
val recipeName = searchRecipeET.text.toString()
searchRecipeName(recipeName, searchRecipeET, bottomSheetDialog)
}
}
}
private fun searchRecipeName(
recipeName: String,
searchRecipeET: EditText,
bottomSheetDialog: BottomSheetDialog
) {
if (recipeName.isEmpty()) {
searchRecipeET.error = "Please enter recipe name"
} else {
recipeViewModel.getRecipes(recipeName)
bottomSheetDialog.dismiss()
}
}
}
RecipeAdapter.kt
class RecipeAdapter(val context: Context) : RecyclerView.Adapter<RecipeAdapter.RecipeViewHolder>() {
private val recipesList: ArrayList<Hit> = arrayListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeViewHolder {
val layoutInflater = LayoutInflater.from(context)
val view = layoutInflater.inflate(R.layout.recipe_items_layout, null, false)
return RecipeViewHolder(view)
}
override fun onBindViewHolder(holder: RecipeViewHolder, position: Int) {
val currentItem = recipesList[position]
holder.recipeImageView.load(currentItem.recipe.image)
holder.recipeNameText.text = currentItem.recipe.label
}
override fun getItemCount(): Int {
return recipesList.size
}
class RecipeViewHolder(itemView: View) :RecyclerView.ViewHolder(itemView) {
val recipeImageView: ImageView = itemView.findViewById(R.id.recipeImageView)
val recipeNameText: TextView = itemView.findViewById(R.id.recipeNameText)
}
fun updateRecipes(newRecipesList: ArrayList<Hit>){
recipesList.clear()
Log.d("RECIPE SIZE", "${recipesList.size}")
recipesList.addAll(newRecipesList)
notifyDataSetChanged()
}
}
This may be helpful.
Be careful of this :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(mainBinding.root)
recipeViewModel =
ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory
.getInstance(application)
)[RecipeViewModel::class.java]
recipeItemList = arrayListOf()
mainBinding.recyclerView.layoutManager = LinearLayoutManager(this)
mainBinding.recyclerView.hasFixedSize()
recipeAdapter = RecipeAdapter(this)
mainBinding.recyclerView.adapter = recipeAdapter
recipeViewModel.recipeLiveData.observe(this, Observer { recipeItems ->
// You're adding items here but never clear the list
// list will be bigger every time you'll be notified
// recipeItemList.addAll(recipeItems.hits
// recipeAdapter.updateRecipes(recipeItemList)
// Do this instead
recipeItemList = recipeItems.hits
recipeAdapter.updateRecipes(recipeItemList)
Log.d("RESPONSE", recipeItems.toString())
Log.d("List size", recipeAdapter.itemCount.toString())
})
searchRecipeName()
}
Also, here: It's a little better to do this (https://stackoverflow.com/a/10298038/4221943)
fun updateRecipes(newRecipesList: ArrayList<Hit>){
recipesList = newRecipesList
Log.d("RECIPE SIZE", "${recipesList.size}")
notifyDataSetChanged()
}
BTW it will always be more efficient to use the more specific change events if you can. Rely on notifyDataSetChanged() as a last resort. It is also good practice to use notifyItemInserted(mItems.size() - 1) for "easier" solution.
You could convert the RecyclerView.Adapter into a ListAdapter:
class RecipeAdapter(val context: Context) : ListAdapter<Hit, RecipeAdapter.RecipeViewHolder>(RecipeDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeViewHolder {
val layoutInflater = LayoutInflater.from(context)
val view = layoutInflater.inflate(R.layout.recipe_items_layout, null, false)
return RecipeViewHolder(view)
}
override fun onBindViewHolder(holder: RecipeViewHolder, position: Int) {
val currentItem = getItem(position)
holder.recipeImageView.load(currentItem.recipe.image)
holder.recipeNameText.text = currentItem.recipe.label
}
class RecipeViewHolder(itemView: View) :RecyclerView.ViewHolder(itemView) {
val recipeImageView: ImageView = itemView.findViewById(R.id.recipeImageView)
val recipeNameText: TextView = itemView.findViewById(R.id.recipeNameText)
}
}
class RecipeDiffCallback : DiffUtil.ItemCallback<Hit>() {
// Change this condition based on the attribute of `Hit` that will change
override fun areItemsTheSame(oldItem: Hit, newItem: Hit): Boolean = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Hit, newItem: Hit): Boolean = oldItem == newItem
}
Then update its content with the submitList method.
Every item not satisfying the RecipeDiffCallback conditions will be automatically updated:
recipeViewModel.recipeLiveData.observe(this, Observer { recipeItems ->
recipeAdapter.submitList(recipeItems.hits)
})
I have add an some condition in viewholder of recycleradapter of recyclerview to hide some items
but it shows empty spaces of removed items in recyclerview which I hide how can I solve these problem to remove empty spaces.is there any another way to hide the items in recyclerview if is then share us.
Bookadapter.kt
class bookadapter(
private var booklist: ArrayList<Booksmodel>,
private val itemClickListener: OnBookItemClicklistner
) : RecyclerView.Adapter<bookadapter.bookholder>() {
var searchText: String = ""
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): bookholder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.singlebook, parent, false)
return bookholder(view)
}
fun filterlist(filterlist: ArrayList<Booksmodel>, searchText: String) {
this.searchText = searchText
booklist = filterlist
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: bookholder, position: Int) {
val view = booklist[position]
holder.dind(view, itemClickListener)
}
override fun getItemCount(): Int {
return booklist.size
}
inner class bookholder(view: View) : RecyclerView.ViewHolder(view) {
val bookname: TextView = view.findViewById(R.id.recbooknametxt)
val bookpublication = view.findViewById<TextView>(R.id.recbookpubtxt)
val bookdept = view.findViewById<TextView>(R.id.recbookdepttxt)
val bookimage = view.findViewById<ImageView>(R.id.recbookimg)
val bookview = view.findViewById<CardView>(R.id.bookcardView)
fun bind(book: Booksmodel, clicklistner: OnBookItemClicklistner) {
val database = FirebaseDatabase.getInstance()
val auth = FirebaseAuth.getInstance()
database.getReference("Users").child(book.UserUID.toString())
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.value != null) {
val usercollege = snapshot.child("College").value.toString()
database.getReference("Users")
.child(auth.currentUser!!.uid)
.addListenerForSingleValueEvent(object :
ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.value != null) {
val mycollege =
snapshot.child("College").value.toString()
if (usercollege == mycollege) {
if (searchText.isNotBlank()) {
val highlightedText = book.BookName!!.replace(
searchText,
"<font color='red'>$searchText</font>",
true
)
bookname.text =
HtmlCompat.fromHtml(
highlightedText,
HtmlCompat.FROM_HTML_MODE_LEGACY
)
} else {
bookname.text = book.BookName
}
//bookname.text=book.BookName
bookpublication.text = book.BookPublication
bookdept.text = book.Department
Picasso.get().load(book.BookImage).into(bookimage)
} else {
bookview.visibility = View.GONE
}
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
}
override fun onCancelled(error: DatabaseError) {
}
})
itemView.setOnClickListener {
clicklistner.onBookItemclick(book)
}
}
}
interface OnBookItemClicklistner {
fun onBookItemclick(books: Booksmodel)
}
}
Are you using Android studio?. It should have caught such errors.
override fun onBindViewHolder(holder: bookholder, position: Int) {
val view = booklist[position]
//holder.dind(view, itemClickListener) typo
holder.bind(view, itemClickListener)
}
If you want to hide the item completely
bookview.visibility = View.GONE
bookview.layoutParams = ViewGroup.LayoutParams(0,0) //sets width and height of the view
I'm working on a firebase realtime database android project. I build a feed system that shows all the users posts but it sorts all data at once. I need to show 10 posts then show progress bar then load 10 others etc...
Explore Fragment
class ExploreFragment : Fragment() {
private var postAdapter:PostAdapter? = null
private var postList: MutableList<Post>? = null
override fun onCreateView(
recyclerView.layoutManager = linearLayoutManager
postList= ArrayList()
postAdapter = context?.let { PostAdapter(it,postList as ArrayList<Post>) }
recyclerView.adapter = postAdapter
retrievePosts()
return view
}
private fun retrievePosts() {
val postRef = FirebaseDatabase.getInstance().reference.child("Posts")
postRef.addValueEventListener(object :ValueEventListener{
override fun onDataChange(p0: DataSnapshot) {
if (p0.exists()){
postList!!.clear()
for (snapshot in p0.children){
val post = snapshot.getValue(Post::class.java)!!
(postList as ArrayList<Post>).add(post)
}
postAdapter?.notifyDataSetChanged()
}
}
override fun onCancelled(p0: DatabaseError) {
}
})
}}
PostAdapter
class PostAdapter(private val mContext: Context,private val mPost: List<Post>
):RecyclerView.Adapter<PostAdapter.ViewHolder>()
{
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(mContext).inflate(R.layout.post_layout, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val post = mPost[position]
Picasso.get().load(post.getPost()).into(holder.postImg)
publisherInfo(holder.profileImg, holder.userName, post.getPublisher())
}
override fun getItemCount(): Int {return mPost.size}
inner class ViewHolder(#NonNull itemView: View): RecyclerView.ViewHolder(itemView)
{
var profileImg:CircleImageView
var postImg:ImageView
var userName:TextView
init {
profileImg = itemView.findViewById(R.id.profile_image_post)
postImg = itemView.findViewById(R.id.post_image)
userName = itemView.findViewById(R.id.username_post)
}
}
private fun publisherInfo(profileImg: CircleImageView, userName: TextView, publisherID: String) {
val userRef = FirebaseDatabase.getInstance().reference.child("Users").child(publisherID)
userRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
if (p0.exists()) {
val user = p0.getValue<User>(User::class.java)
Picasso.get().load(user!!.getImage()).into(profileImg)
userName.text = user!!.getUsername()
}
}
override fun onCancelled(p0: DatabaseError) {
}
})
}
}
I remove some lines to make the code simple
I use Firebase Realtime Database. I have RecycleViewAdapter class, a Fragment which shows the recycleview, a ViewHolder class which gives data to the fragment (I use LiveData) and there is a Repository class which communicates with the firebase database.
class Post(
var title: String = "",
var dateStr: String = "",
var desc: String = "",
var id: String = "")
class PostsFireBaseRepository(val bucketListViewModel: BucketListViewModel) {
private val path: String = "posts"
init {
initPostsListener()
}
fun getAllPosts() {
var data = mutableListOf<Post>()
FirebaseDatabase.getInstance().reference
.child(FirebaseAuth.getInstance().currentUser?.uid.toString())
.child(path)
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val newPost = dataSnapshot.getValue<Post>(Post::class.java)
if (newPost != null) {
data.add(newPost)
}
}
bucketListViewModel.allPosts.value = data
bucketListViewModel.dataChanged()
}
override fun onCancelled(error: DatabaseError) {
}
})
}
class BucketListViewModel() : ViewModel() {
private val repository: PostsFireBaseRepository
var recyclerViewAdapter: BucketListRecyclerViewAdapter? = null
var allPosts: MutableLiveData<MutableList<Post>> = MutableLiveData<MutableList<Post>>()
init {
repository = PostsFireBaseRepository(this)
repository.getAllPosts()
}
fun dataChanged() {
recyclerViewAdapter?.notifyDataSetChanged()
}
}
class BucketListFragment : Fragment(), DatePickerDialogFragment.OnDateSelectedListener,
BucketListRecyclerViewAdapter.PostItemClickListener {
private lateinit var bucketListViewModel: BucketListViewModel
private lateinit var recyclerViewAdapter: BucketListRecyclerViewAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_bucketlist, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRecyclerView()
bucketListViewModel = ViewModelProvider(requireActivity()).get(BucketListViewModel::class.java)
bucketListViewModel.allPosts.observe(viewLifecycleOwner) { posts ->
recyclerViewAdapter.addAll(posts)
}
}
BucketListRecyclerViewAdapter
class BucketListRecyclerViewAdapter() : RecyclerView.Adapter<BucketListRecyclerViewAdapter.ViewHolder>() {
interface PostItemClickListener {
fun onItemLongClick(position: Int, view: View, post: Post?): Boolean
fun onItemClick(position: Int, view: View, post: Post?): Boolean
}
private var postList = mutableListOf<Post>()
private var lastDeleted: Post? = null
private var lastDeletedPos: Int? = null
var postClickListener: PostItemClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.bucketlist_post_row, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val post = postList[holder.adapterPosition]
holder.post = post
holder.tvTitle.text = post.title
holder.tvDesc.text = post.desc
holder.tvRowDate.text = post.dateStr
}
override fun getItemCount() = postList.size
fun addItem(post: Post) {
postList.add(post)
notifyItemInserted(postList.size-1)
}
fun addAll(posts: List<Post>) {
postList.clear()
postList.addAll(posts)
notifyDataSetChanged()
}
In your onDataChange method you will need to inform that adapter that its data is changed, otherwise it won't repaint the UI element. To do this, you call notifyDataSetChanged() on the adapter after you've updated the data source for that adapter.
So something like:
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val newPost = dataSnapshot.getValue<Post>(Post::class.java)
if (newPost != null) {
data.add(newPost)
}
}
bucketListViewModel.allPosts.value = data
adapter.notifyDataSetChanged();
}
Exactly how to reach the adapter depends on your code. For example, in your case you may have to add a method to the BucketListViewModel that you call from onDataChange and that then calls the adapter.
I have been trying for few days to find a solution , but I can not find the hot potato.
I have about 200 users in attendance list for a sports club.
The checkbox should store which users are present.
The problem is that I can only manage only one user.
I try a lot but no result
Anybody have an idea how to solve this problem please?
UPDATE:
Thanks to
#Sanlok Lee
Problem successfully resolved.
Below the new code:
class User:
class User(
val uid: String?,
val username: String?,
val profileImageUrl: String?,
val isSelected: Boolean?
) {
constructor() : this("", "", "", false)
}
class Home:
class Home : AppCompatActivity() {
val db = FirebaseFirestore.getInstance()
val adapter = PerformListAdapter()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_perform_anwesenheitsliste)
verifyUserIsLogin()
_recyclerView_show_anwesenheitsliste.adapter = adapter
showUsersAndPerformChekBoxes()
}
fun showUsersAndPerformChekBoxes() {
db.collection("users")
.get()
.addOnSuccessListener { documents ->
val users = documents.map { doc ->
doc.toObject(User::class.java)
}
adapter.submitUsers(users)
}
}
private fun verifyUserIsLogin() {
val uid = FirebaseAuth.getInstance().uid
if (uid == null) {
val intent = Intent(this, LoginActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK.or(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.menu_home -> {}
R.id.menu_sign_out -> {
FirebaseAuth.getInstance().signOut()
val intent = Intent(this, RegisterActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK.or(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
}
return super.onOptionsItemSelected(item)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.nav_menu_home, menu)
return super.onCreateOptionsMenu(menu)
}
}
class PerformListAdapter:
class PerformListAdapter : RecyclerView.Adapter<PerformListAdapter.CustomViewHolder>() {
val db = FirebaseFirestore.getInstance()
var users: List<User> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
val inflater = LayoutInflater.from(parent.context)
val itemuViewRow = inflater.inflate(R.layout.user_row, parent, false)
return CustomViewHolder(itemuViewRow)
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
if (users.size > position) {
holder.bindTo(users[position])
} else {
holder.clear()
}
}
override fun getItemCount(): Int {
return users.size
}
fun submitUsers(users: List<User>) {
this.users = users
notifyDataSetChanged()
}
inner class CustomViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val checkBox_select: CheckBox = view.findViewById(R.id._checkBox_select)
private val textView_anwesenheitsliste_username =
view.findViewById<TextView>(R.id._textView_anwesenheitsliste_username)
private val imageView_show_anwesenheitsliste_row =
view.findViewById<CircleImageView>(R.id._imageView_show_anwesenheitsliste_row)
private val checkedChangeListener = CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
val uid = users[adapterPosition].uid
if (uid != null) {
Log.d(TAG, uid)
if (isChecked) {
db.collection("users")
.document(uid)
.update("selected", true)
} else {
db.collection("users")
.document(uid)
.update("selected", false)
}
}
}
fun bindTo(user: User) {
if (!user.profileImageUrl!!.isEmpty()) {
Picasso.get().load(user.profileImageUrl)
.into(imageView_show_anwesenheitsliste_row)
}
textView_anwesenheitsliste_username?.text = user.username
checkBox_select.setOnCheckedChangeListener(null)
checkBox_select.isChecked = (user.isSelected == true)
checkBox_select.setOnCheckedChangeListener(checkedChangeListener)
}
fun clear() {
textView_anwesenheitsliste_username.text = ""
checkBox_select.setOnCheckedChangeListener(null)
}
}
}
The first thing to think about is whether you need a click listener for this. The click event should be absorbed by the CheckBox and therefore click listener is probably not needed. Also, setOnCheckedChangeListener should be set once per CheckBox not per click nor per User items.
Since you haven't showed us GroupAdapter nor ViewHolder implementation, so I just came up with one possible implementation:
GroupAdapter.kt
class GroupAdapter: RecyclerView.Adapter<GroupAdapter.ViewHolder>() {
private val db = FirebaseFirestore.getInstance()
private var users: List<User> = ArrayList() // Store user list here
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val itemView = inflater.inflate(R.layout.yourViewHolderLayout, parent, false)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
if (users.size > position)
holder.bindTo(users[position])
else
holder.clear()
}
override fun getItemCount() = users.size
// Update the user list
fun submitUsers(users: List<User>) {
this.users = users
notifyDataSetChanged()
}
inner class ViewHolder(view: View): RecyclerView.ViewHolder(view) {
private val _checkBox_select: CheckBox = view.findViewById(R.id.yourCheckBoxId)
private val _textView_anwesenheitsliste_username = view.findViewById(R.id.yourTextBoxId)
private val checkedChangeListener =
CompoundButton.OnCheckedChangeListener { view, isChecked ->
val uid = users[adapterPosition].uid
if (uid != null) {
if (isChecked) {
db.collection("users").document(uid)
.update("isSelected", true)
} else {
db.collection("users").document(uid)
.update("isSelected", false)
}
}
}
// bind to user here
fun bindTo(user: User) {
if (!user.prfileImageUrl!!.isEmpty()) {
Picasso.get().load(user.prfileImageUrl)
.into(_imageView_show_anwesenheitsliste_row)
}
_textView_anwesenheitsliste_username.text = user.username
_checkBox_select.setOnCheckedChangeListener(null)
_checkBox_select.isChecked = (user.isSelected == true)
_checkBox_select.setOnCheckedChangeListener(checkedChangeListener)
}
// clear
fun clear() {
_textView_anwesenheitsliste_username.text = ""
_checkBox_select.setOnCheckedChangeListener(null)
}
}
}
Then you can use this adapter in your activity like this:
val db = FirebaseFirestore.getInstance()
val adapter = GroupAdapter()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_perform_anwesenheitsliste)
_recyclerView_show_anwesenheitsliste.adapter = adapter
showUsersAndPerformChekBoxes()
}
private fun showUsersAndPerformChekBoxes() {
db.collection("users")
.get()
.addOnSuccessListener { documents ->
val users = documents.map { doc -> doc.toObejct(User::Class.java) }
adapter.submitUsers(users)
}
}
Things might not work straight off since I haven't tested the code.
Further improvements
If you are interested, think about these scenarios: What if there are too many users that the app cannot get the entire list all at once? What if there are multiple phones that run the same app and try to update the list all at the same time? What if there is a network problem? What happens if the user spams the checkbox x9999 times?
The solutions for these problems are not trivial so I am not answering here, but it is a good practice to think about edge cases and how the app should react to those scenarios.