RecyclerView OnCheckedChangeListener bug when click TabLayout - android

so I have TabLayout with RecyclerView, when I click the tab it reloads the RecyclerView with new sets of data, but I'm having a problem with onCheckedChangeListener.
When I change the tab it reloads the new set of data but it also triggers the onCheckedChangeListener too.
here's the code that I have
ADAPTER
class ProductAdapter(
private val product: ArrayList<ProductDataRs>
) : RecyclerView.Adapter<ProductAdapter.DataViewHolder>() {
var onItemClick: ((ProductDataRs) -> Unit)? = null
inner class DataViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(productDataRs: ProductDataRs) {
itemView.text_view_product_name.text = productDataRs.name.toString()
itemView.text_view_description.text = productDataRs.description.toString()
itemView.text_view_price.text = "Php ${productDataRs.price.toString()}"
itemView.switch_product.isChecked = productDataRs.status != 0
itemView.switch_product.setOnCheckedChangeListener { compoundButton: CompoundButton, b: Boolean ->
onItemClick?.invoke(product[adapterPosition])
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
DataViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.item_product_layout, parent,
false
)
)
override fun getItemCount(): Int = product.size
override fun onBindViewHolder(holder: DataViewHolder, position: Int) {
holder.bind(product[position])
}
FRAGMENT
private fun setupUI() {
fragmentProductsBinding.recyclerViewProducts.layoutManager =
LinearLayoutManager(activity?.applicationContext)
productAdapter = ProductAdapter(arrayListOf())
fragmentProductsBinding.recyclerViewProducts.adapter = productAdapter
productAdapter.onItemClick = { product ->
Log.e("asd", "asd")
// updateProductStatus(product.id!!, if (product.status == 0) 1 else 0)
}
fragmentProductsBinding.tabLayoutProducts.addOnTabSelectedListener(object :
TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
var iterator = (data)?.iterator()
if (iterator != null) {
for ((index, rs) in iterator.withIndex()) {
if (index == tab.position) {
rs.products?.let { retrieveList(it) }
}
}
} else {
Log.e("ProductsFragment", "no data")
}
}
})
}

If you want to trigger it when it is on(isChecked) you should change your code to :
itemView.switch_product.setOnCheckedChangeListener { compoundButton: CompoundButton, b: Boolean ->
if(b) onItemClick?.invoke(product[adapterPosition])
}
If else you want to trigger it when it is off(unChecked) you should change your code to :
itemView.switch_product.setOnCheckedChangeListener { compoundButton: CompoundButton, b: Boolean ->
if(!b) onItemClick?.invoke(product[adapterPosition])
}

you can combine the TabLayout and viewPager2 and handle the recycler view in that fragment :
https://developer.android.com/guide/navigation/navigation-swipe-view-2
i did the similar thing on this project i leave it down below if you want more examples:
https://github.com/ErfanDP/Erfan_Delavari_HW12_Maktab36
at first enter random name that doesn't matter

Related

How to select all checkboxes in recyclerview on click?

I display several items in the recyclerview, each item has its own checkbox, I want to select the necessary ones and delete them. I did this part and it works well, but I can’t do select all items, please tell me what’s wrong.
Here is my code:
initRecyclerView
private fun initRecyclerView() {
binding.recyclerView.setHasFixedSize(true)
val adapter = AdapterList(requireActivity(), arrayList)
binding.recyclerView.layoutManager = LinearLayoutManager(requireActivity().applicationContext, LinearLayoutManager.VERTICAL, false)
binding.recyclerView.adapter = adapter
}
My function for checkbox in fragment:
binding.allCheckBox.setOnClickListener {
if (isAllSelect) {
AdapterList().unselectAll()
isAllSelect = false
} else {
AdapterList().selectAll()
isAllSelect = true
}
}
My adapter
class AdapterList(private val activity: Activity? = null, private var recyclerList: ArrayList<Info>? = null) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
return (holder as ViewHolder).bind(recyclerList!![position])
}
override fun getItemCount(): Int {
return recyclerList!!.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ViewHolder(LayoutInflater.from(activity).inflate(R.layout.item_layout, parent, false))
}
private var isSelectedAll = false
#SuppressLint("NotifyDataSetChanged")
fun selectAll() {
isSelectedAll = true
notifyDataSetChanged()
}
#SuppressLint("NotifyDataSetChanged")
fun unselectAll() {
isSelectedAll = false
notifyDataSetChanged()
}
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private var binding: ItemLayoutBinding = ItemLayoutBinding.bind(view)
#SuppressLint("NotifyDataSetChanged")
fun bind(list: Info) {
binding.apply {
cardIcon.setImageDrawable(list.icon)
cardName.text = list.name
cardCheckBox.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
recyclerList!![position].checked = isChecked
notifyDataSetChanged()
}
cardCheckBox.isChecked = isSelectedAll
}
}
}
}
Now it crashes on clicking on the checkbox in cardview because of the line
cardCheckBox.isChecked = isSelectedAll
in Adapter
Tried to do as described here but does not work Select all checkboxes in RecyclerView
If I do so
binding.allCheckBox.setOnClickListener {
if (isAllSelect) {
for (list in arrayList) {
list.checked = false
}
isAllSelect = false
} else {
for (list in arrayList) {
list.checked = true
}
isAllSelect = true
}
}
By clicking on the delete button, I see that the data is correct, but the checkbox in the recyclerview is not updated (notifyDataSetChanged() does not work?), and I'm not sure if this approach is correct

On RecyclerView scroll random checkbox selected in kotlin

*Note: There are solutions for this issue in stackoverflow but all are written in java
In Recyclerview implemented checkbox OnCheckedChangeListener as follows, problem is when i scroll recyclerview then random checkbox is selected.
class ScSerialAdapter(
private val scSerialNumberList: List<String>,
private val selectedSerialNumberList: ArrayList<String> = arrayListOf<String>()
) :
RecyclerView.Adapter<ScSerialAdapter.ScLocationListViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ScLocationListViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.stock_serials_list_item, parent, false)
return ScLocationListViewHolder(itemView)
}
override fun onBindViewHolder(holder: ScLocationListViewHolder, position: Int) {
holder.cbSelected.isChecked = false
val currentSerialNo = this.scSerialNumberList[position]
for (item in selectedSerialNumberList) {
if (item == currentSerialNo) {
holder.cbSelected.isChecked = true
break
}
}
holder.cbSelected.setOnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
selectedSerialNumberList.add(this.scSerialNumberList[position])
Log.e("selectedSerialNumber",selectedSerialNumberList.size.toString())
} else {
Log.e("SelectedSerialNumber",selectedSerialNumberList.size.toString())
selectedSerialNumberList.remove(this.scSerialNumberList[position])
}
}
if (scSerialNumberList != null) {
holder.tvSerial.text = scSerialNumberList[position].toString()
} else {
holder.tvSerial.text = "-"
}
}
fun listOfSelectedValues(): List<String> {
return selectedSerialNumberList
}
override fun getItemCount(): Int {
return scSerialNumberList.size
}
inner class ScLocationListViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvSerial: TextView = itemView.tv_serial
val cbSelected: CheckBox = itemView.cb_selected
}
}
You need to set the checkbox value every time the onBindViewHolder() get called with a new row in the RecyclerView
override fun onBindViewHolder(holder: PickListViewHolder, position: Int) {
if (selectedSerialNumberList.contains(this.scSerialNumberList[position])) {
holder.cbSelected.isChecked = true
} else {
holder.cbSelected.isChecked = false
}
holder.cbSelected.setOnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
selectedSerialNumberList.add(this.scSerialNumberList[position])
} else {
selectedSerialNumberList.remove(this.scSerialNumberList[position])
}
}
}
UPDATE
Try to iterate on the list and check if the serial number exist.
override fun onBindViewHolder(holder: PickListViewHolder, position: Int) {
holder.cbSelected.isChecked = false
val currentSerialNo = this.scSerialNumberList[position]
for (item in selectedSerialNumberList) {
if (item == currentSerialNo) {
holder.cbSelected.isChecked = true
break
}
}
//...
}
In onBindViewHolder made following changes, It worked
override fun onBindViewHolder(holder: ScLocationListViewHolder, position: Int) {
holder.cbSelected.setOnCheckedChangeListener(null)
holder.cbSelected.isChecked = selectedSerialNumberList.contains(this.scSerialNumberList[position])
holder.cbSelected.setOnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
selectedSerialNumberList.add(this.scSerialNumberList[position])
Log.e("selectedSerialNumber", selectedSerialNumberList.size.toString())
} else {
Log.e("SelectedSerialNumber", selectedSerialNumberList.size.toString())
selectedSerialNumberList.remove(this.scSerialNumberList[position])
}
}
if (scSerialNumberList != null) {
holder.tvSerial.text = scSerialNumberList[position].toString()
} else {
holder.tvSerial.text = "-"
}
}

chekbox always checking last item in recyclerview

i want to get item from checked checkbox in my recyclerview item, this my adapter
class SelectedListDateAdapter(var listDate: List<DateDay>, private val onItemCheckListener: OnItemCheckListener) :
RecyclerView.Adapter<SelectedListDateAdapter.SelectedListDateViewHolder>() {
lateinit var binding: ItemCheckBoxDateBinding
inner class SelectedListDateViewHolder(item: ItemCheckBoxDateBinding) : RecyclerView.ViewHolder(item.root) {
val checkBoxList = item.checkBox
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SelectedListDateViewHolder {
binding = ItemCheckBoxDateBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return SelectedListDateViewHolder(binding)
}
override fun onBindViewHolder(holder: SelectedListDateViewHolder, position: Int) {
holder.checkBoxList.setOnCheckedChangeListener(null)
holder.checkBoxList.isChecked = listDate[position].isSelected
holder.itemView.apply {
val currentItem = listDate[position]
binding.tvDateList.text = listDate[position].date
setOnClickListener {
binding.checkBox.isChecked = !binding.checkBox.isChecked
if (binding.checkBox.isChecked) {
binding.checkBox.setOnCheckedChangeListener { buttonView, isChecked ->
currentItem.isSelected = isChecked
}
onItemCheckListener.onItemCheck(currentItem)
} else {
binding.checkBox.setOnCheckedChangeListener { buttonView, isChecked ->
currentItem.isSelected = isChecked
}
onItemCheckListener.onItemUncheck(currentItem)
}
}
}
}
override fun getItemCount(): Int {
return listDate.size
}
}
im referring to this question get list of checked item to make that adapter
yes, it get the item and remove them but everytime i click an item in recyclerview it always check and uncheck the last item
i have checking this question CheckBox in RecyclerView keeps on checking different items but my result still the same, any help is appreciated
Maybe viewHolder reuse the previous item.Try to update listData, not currentItem.
And move the nested Listener
override fun onBindViewHolder(holder: SelectedListDateViewHolder, position: Int) {
holder.itemView.tvDateList.text = listDate[position].date
holder.checkBoxList.isChecked = listDate[position].isChecked
holder.checkBoxList.setOnClickListener {
listDate[position].isSelected = holder.checkBoxList.isChecked
}
holder.itemView.setOnClickListener {
holder.checkBoxList.isChecked = !holder.checkBoxList.isChecked
listDate[position].isSelected = holder.checkBoxList.isChecked
val currentItem = listDate[position]
if (holder.checkBoxList.isChecked) {
onItemCheckListener.onItemCheck(currentItem)
} else {
onItemCheckListener.onItemUncheck(currentItem)
}
}
}

Check box in recyclerview is automatically unchecked when scrolling in android

I have created a view using recyclerview adapter for listing students. My problem is that when i am scrolling the view all the checked checkboxes are automatically unchecked. Which means the states are changing.How to overcome the issue
Here i shared my code. Please check and suggest a solution. Thanks in advance.
class IndividualStudentSelectAdapter(private val context: Context,
private var students: List<IndividualStudentBean>,
private val checkBox1: CheckBox,
private val individualStudentSelectionListener: IndividualStudentSelectionListener
)
: RecyclerView.Adapter<IndividualStudentSelectAdapter.ViewHolder>(){
companion object {
val TAG: String = IndividualStudentSelectAdapter::class.java.simpleName
}
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): IndividualStudentSelectAdapter.ViewHolder {
return ViewHolder(LayoutInflater.from(context).inflate(R.layout.individual_student, p0, false))
}
override fun getItemCount(): Int {
return students.size
}
override fun onBindViewHolder(p0: ViewHolder, p1: Int) {
val user = students.get(p1)
p0.bindView(p1)
p0.binding.individualStudentBean = students[p1]
if (user.getSelected()==true) {
p0.checkbox2.isChecked = true
} else {
p0.checkbox2.isChecked = false
}
p0.checkbox2.setOnCheckedChangeListener { buttonView, isChecked ->
if(isChecked)
{
// val individualStudentBean = IndividualStudentBean(user.empid,"","")
Log.d("admnos",user.id)
individualStudentSelectionListener.addDatas(user.id)
user.setSelected(true)
}else{
individualStudentSelectionListener.removeDatas(user.id)
user.setSelected(false)
}
}
checkBox1.setOnCheckedChangeListener {
buttonView, isChecked ->
if (isChecked){
for (item in students)
{
p0.checkbox2.isChecked = true
item.setSelected(true)
notifyDataSetChanged()
}
}else{
for (item in students) {
p0.checkbox2.isChecked = false
item.setSelected(false)
notifyDataSetChanged()
}
}
}
}
fun updateDataset(students: List<IndividualStudentBean>) {
this.students = students
notifyDataSetChanged()
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var binding: IndividualStudentBinding = DataBindingUtil.bind(itemView)!!
val checkbox2 = itemView.findViewById(R.id.cb_student) as CheckBox
fun bindView(position: Int) {
// this.setIsRecyclable(false)
binding.individualStudentBean = students[position]
itemView.setOnClickListener {
//students[position].selected = itemView.cb_student.isChecked
if (context is IndividualStudentSelectActivity) {
//context.onStudentSelected()
}
if (context is IndividualStudentSelectActivity) {
//context.onStudentSelected()
}
}
}
}
}
add a Property called isChecked (the name depends on your choice) to your Student data class, then add this code in bindView method
checkbox2.isChecked = students[adapterPosition].isChecked
checkbox2.setOnCheckedChangeListener { _, isChecked ->
students[adapterPosition].isChecked = isChecked
}

How to show single item selected in recyclerview using kotlin

How can we mark single item is selected in Recyclerview using kotlin. When I select an item and after that click on other item then previously selected item should be dis-selected.Here is my adapter class in kotlin:..
class ListAdapter(var context: Context, var list: ArrayList<ListModel>) : RecyclerView.Adapter<ListAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MyViewHolder {
val v = LayoutInflater.from(parent?.context).inflate(R.layout.list_item, parent, false)
return MyViewHolder(v)
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: MyViewHolder?, position: Int) {
holder?.bindItems(list[position])
}
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view){
fun bindItems(items: ListModel) {
itemView.txt_que.text = items.que
itemView.txt_ans.text = items.ans
itemView.txt_sr_no.text = items.srNo
}
}`
Here dataItem is Model Class and please take one extra Boolean variable isSelected in model class (default value is false) and true when select item then true this variable on selected position,
below are example :
class AllModuleListAdapter(
var context: Context?,
private var moduleList: ArrayList<DataItem?>?,
private var mCallback: DeviceClickListener
) :
RecyclerView.Adapter<AllModuleListAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val context = parent.context
val itemView = LayoutInflater.from(context).inflate(R.layout.item_modules, parent, false)
return MyViewHolder(itemView)
}
override fun getItemCount(): Int {
return moduleList?.size ?: 0
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(moduleList?.get(position))
if (moduleList?.get(position) is DataItem) {
val dataItem = moduleList?.get(position) as DataItem
if (dataItem.isSelected) {
context?.let { ContextCompat.getColor(it, R.color.colorOrange) }
?.let { holder.itemView.setBackgroundColor(it) }
} else {
context?.let { ContextCompat.getColor(it, R.color.white) }
?.let { holder.itemView.setBackgroundColor(it) }
}
}
}
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
init {
itemView.constraint_main.setOnClickListener {
val list = moduleList as List<DataItem>
for (item in list.indices) {
list[item].isSelected = false
}
list[adapterPosition].isSelected = true
mCallback.onDeviceClick(adapterPosition)
notifyDataSetChanged()
context?.let { it1 -> ContextCompat.getColor(it1, R.color.colorOrange) }?.let { it2 ->
itemView.constraint_main?.setBackgroundColor(it2)
}
}
}
fun bind(module: DataItem?) {
itemView.txtModuleName?.text = module?.id.toString()
itemView.txtSignal?.text = module?.room
}
}
}
if (mPosition == position)
{
//set selected here
} else
{
//set unselected here
}
holder.parentView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
mPosition = position;
notifyDataSetChanged();
}
});
Write above code in onBindViewholder and declare mPosition as global int variable in adapter class
Try with this:- take one variable in your ListModel class as
var selected:boolean = false
then while setting the listModel items set this value as false as
for(int i=0;i<listModel.size;i++){
listModel.get(i).selected = false
}//this is for setting all values false
when you select any item from list call this method and then set selected = true for selected position and simply refresh the adapter list.
in your adapter check for this selected value and accordingly set the check box value inside your bindItems method
itemView.checkBox.selected = items.selected//this will set your checkbox selected value

Categories

Resources