Select only one check box ; RecyclerView; Kotlin - android

It's my code and I want to choose only one checkbox, and if another checkbox is selected I want that this convert in uncheked
class AuthorizationFormAdapter(lista: List<Procedures>?) : RecyclerView.Adapter<AuthorizationFormAdapter.ViewHolder>() {
val lista: List<Procedures>? = lista
lateinit var array: BooleanArray
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val text: TextView? = null
val checkBox: CheckBox? = null
}
override fun onCreateViewHolder(parent: ViewGroup, p1: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.card_authform, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return lista!!.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.view.auth_name.text = lista!![position].name_procedure
holder.view.cb_auth.setOnClickListener {
if (holder.view.cb_auth.isChecked) {
holder.view.cb_auth.isSelected = true
} else if (!holder.view.cb_auth.isChecked) {
holder.view.cb_auth.isChecked = false
}
}
}
}
And Xml is only one checkBox and textView in my card
I will be very grateful with your help

You have to create a field in your Procedures to store the checked/unchecked state
var isChecked: Boolean = false
Then inside your adapter store lastCheckedPosition and unchecked when user select other.
class AuthorizationFormAdapter(lista: List<Procedures>?) : RecyclerView.Adapter<AuthorizationFormAdapter.ViewHolder>() {
var lastCheckedPosition: Int = 0
....
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val procedure = lista!![position]
holder.view.auth_name.text = procedure.name_procedure
holder.view.cb_auth.isChecked = procedure.is_checked
holder.view.cb_auth.setOnCheckedChangeListener { buttonView, isChecked ->
if(isChecked) {
lista!![lastCheckedPosition].is_checked = false
lastCheckedPosition = position
}
lista!![position].is_checked = isChecked
notifyDataSetChanged() // This is important to reload the list
}
}
}

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

A recyclerView with subitems (nested) is not showing the subitems when I initialize the Adapter, but only when the section is clicked

I saw different tutorials on the internet to create a nested recyclerView. It works as expected but all the items are collapsed when I initialize the adapter, while I need to show all of them opened:
Namely at the moment I have:
Category 1
Category 2
Category 3
Whereas I want :
Category1
Dish1
Dish2
Category2
Dish 1
Category 3
Dish 1
Dish2
I have an handle can click on every Category, for instance Category 1, and a click listener loads the adapter on the basis of the position, but I want to open all the items when the adapter is initialized
I tried also to furnish from the view that calls the Recyclerview a callback from onLayoutLoaded, but if I call the expand method, from the view it works only if i furnish one position ex. expandRow(0) For instance to open the first item, but if I call all the positions on the basis of the count, the size of the list just does not work
Here my adapter with at the end of the class attached the datamodel
class CategoryListExpandableAdapter(var context: Context, var categoriesList:MutableList<ExpandableCategoryModel>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var shouldCategoryExpanded by Delegates.notNull<Boolean>()
var presentPosition by Delegates.notNull<Int>()
init {
shouldCategoryExpanded = true
}
val parentLayout = R.layout.parent_category_item
val childLayout = R.layout.child_dish_item
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when(viewType) {
parentLayout -> {ParentCategoryViewHolder(LayoutInflater.from(parent.context).inflate(
parentLayout, parent, false))}
else -> { ChildDishViewHolder(LayoutInflater.from(parent.context).inflate(
childLayout, parent, false)) }
}
}
override fun getItemCount(): Int = categoriesList.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val row = categoriesList[position]
presentPosition = position
when(row.type){
CATEGORY_PARENT -> {
(holder as ParentCategoryViewHolder).categoryName.text = row.categoryParent.categoryName
arrowDownButtonListener(holder, row, position)
upArrowButtonListener(holder, row, position)
}
DISH_CHILD -> {
(holder as ChildDishViewHolder).descriptionDishText.text = row.dishChild.description
holder.nameDishText.text = row.dishChild.name
}
}
}
private fun upArrowButtonListener(holder: ParentCategoryViewHolder, row: ExpandableCategoryModel, position: Int) {
holder.upArrowImage.setOnClickListener {
collapseRow(position)
holder.upArrowImage.visibility = View.GONE
holder.downArrowImage.visibility = View.VISIBLE
}
}
private fun arrowDownButtonListener(holder: ParentCategoryViewHolder, row: ExpandableCategoryModel, position: Int) {
holder.downArrowImage.setOnClickListener {
holder.upArrowImage.visibility = View.VISIBLE
holder.downArrowImage.visibility = View.GONE
expandRow(position)
}
}
override fun getItemViewType(position: Int): Int {
return when (categoriesList[position].type) {
1 -> parentLayout
else -> childLayout
}
}
private fun expandRow(position: Int){
val row = categoriesList[position]
var nextPosition = position
when (row.type) {
CATEGORY_PARENT -> {
for(dishes in row.categoryParent.dishesList){
categoriesList.add(++nextPosition, ExpandableCategoryModel(DISH_CHILD, dishes))
}
notifyDataSetChanged()
}
DISH_CHILD -> {
notifyDataSetChanged()
}
}
}
private fun collapseRow(position: Int){
val row = categoriesList[position]
var nextPosition = position + 1
when (row.type) {
CATEGORY_PARENT -> {
outerloop# while (true) {
if (nextPosition == categoriesList.size || categoriesList[nextPosition].type == CATEGORY_PARENT) {
break#outerloop
}
categoriesList.removeAt(nextPosition)
}
notifyDataSetChanged()
}
}
}
class ParentCategoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
internal var categoryName : TextView = itemView.category_name
internal var downArrowImage = itemView.arrow_down
internal var upArrowImage = itemView.up_arrow
}
class ChildDishViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
internal var descriptionDishText : TextView = itemView.child_dish_item_description
internal var nameDishText = itemView.child_dish_item_name
}
}
class ExpandableCategoryModel {
lateinit var categoryParent: Category
var type : Int
lateinit var dishChild : Dish
constructor(type : Int, categoryParent: Category){
this.type = type
this.categoryParent = categoryParent
}
constructor(type : Int, categoryChild : Dish){
this.type = type
this.dishChild = categoryChild
}
}
data class CategoryList(val categories: List<Category>)
data class Category(
val categoryName: String,
val dishesList: List<Dish>
)
data class Dish(
val name: String,
val description: String
)

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)
}
}
}

Adding data from parent recyclerview adapter and removing data from child recyclerview adapter

I have a nested recyclerview. Parent recyclerview adapter can add a data to child by a button, then child recyclerview adapter can remove a data in child by a button. The preview is like below.
Adding data seems fine, but when removing data i got index out of bound. I have called notifyDataSetChanged() in add or remove function.
My parent adapter code :
class FormPlanParentAdapter :
RecyclerView.Adapter<FormPlanParentAdapter.ViewHolder>() {
private val data: MutableList<MutableList<ItemPlan>> = mutableListOf()
private val viewPool = RecyclerView.RecycledViewPool()
fun setData(newData: List<MutableList<ItemPlan>>) {
data.clear()
data.addAll(newData)
notifyDataSetChanged()
}
fun getData(): String = Gson().toJson(data)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_plan_parent, parent, false)
)
}
override fun getItemCount(): Int = data.size
override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
val listPlans = data[position]
val childLayoutManager = LinearLayoutManager(
holder.recyclerView.context,
LinearLayoutManager.VERTICAL,
false
)
val childAdapter = FormPlanChildAdapter(listPlans, object : PlanParentCallback {
override fun deletePlan(position: Int) {
listPlans.removeAt(position)
notifyDataSetChanged()
}
})
holder.recyclerView.apply {
layoutManager = childLayoutManager
adapter = childAdapter
setRecycledViewPool(viewPool)
}
holder.textView.text = "Hari ke ${position + 1}"
holder.imageButton.setOnClickListener {
listPlans.add(ItemPlan())
notifyDataSetChanged()
}
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val recyclerView: RecyclerView = itemView.rv_plan_child
val textView: TextView = itemView.tv_days_of
val imageButton: ImageButton = itemView.ib_add
}
interface PlanParentCallback {
fun deletePlan(position: Int)
}
}
My child adapter code :
class FormPlanChildAdapter(
private val listPlans: List<ItemPlan>,
private val callback: FormPlanParentAdapter.PlanParentCallback
) :
RecyclerView.Adapter<FormPlanChildAdapter.ViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_plan_child, parent, false)
)
}
override fun getItemCount(): Int = listPlans.size
override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
val itemPlan = listPlans[position]
holder.etPlan.setText(itemPlan.plan)
holder.etPlan.doAfterTextChanged {
listPlans[position].plan = it.toString()
}
if (position == 0) {
holder.ibDelete.invisible()
} else {
holder.ibDelete.visible()
}
holder.ibDelete.setOnClickListener {
if (listPlans.size > 1) {
callback.deletePlan(position)
}
}
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val etPlan: EditText = itemView.et_plan
val ibDelete: ImageButton = itemView.ib_delete
}
}
Any help appreciated, thank you.
Reason:
The old TextWatchers and adapter position mess up when the dataset changed.
notifyDatasetChanged triggers onBindViewHolder, and then EditText's TextWatcher is being notified because of this line
holder.etPlan.setText(itemPlan.plan)
Since etPlan has already an active TextWatcher from previous binding, it tries to notify it with the old position of the adapter.
Quick answer:
Keep the TextWatcher in your ViewHolder, remove it from EditText on onBindViewHolder, and set it to the EditText again after setting the EditText's value.
So, update your child adapter's onBindViewHolder as
override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
val itemPlan = listPlans[position]
holder.etPlan.removeTextChangedListener(holder.textWatcher)
holder.etPlan.setText(itemPlan.plan)
holder.textWatcher = holder.etPlan.doAfterTextChanged {
listPlans[position].plan = it.toString()
}
...
}
and also update it's ViewHolder like:
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val etPlan: EditText = itemView.et_plan
val ibDelete: ImageButton = itemView.ib_delete
var textWatcher: TextWatcher? = null
}
But, it's not recommended setting listeners on onBindViewHolder due to performance concerns. Please check this and this.
Here is another workaround:
Move all the TextWatcher things to Adapter via a listener, and prevent notifying the TextWatcher if it's not necessary.
interface TextChangeListener {
fun onTextChange(text: String, position: Int)
}
class FormPlanChildAdapter(...) :
RecyclerView.Adapter<FormPlanChildAdapter.ViewHolder>(), TextChangeListener {
override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
val itemPlan = listPlans[position]
holder.setPlan(itemPlan.plan)
...
}
override fun onTextChanged(text: String, position: Int) {
listPlans[position].plan = text
}
inner class ViewHolder(itemView: View, textChangeListener: TextChangeListener) :
RecyclerView.ViewHolder(itemView) {
private var disableTextChangeListener = false
private val etPlan: EditText = itemView.et_plan
val ibDelete: ImageButton = itemView.ib_delete
init {
etPlan.doAfterTextChanged {
if (!disableTextChangeListener) {
textChangeListener.onTextChanged(it.toString(), adapterPosition)
}
}
}
fun setPlan(plan: String, notifyTextChangeListener: Boolean = false) {
if (notifyTextChangeListener) {
etPlan.setText(plan)
} else {
disableTextChangeListener = true
etPlan.setText(plan)
disableTextChangeListener = false
}
}
}
}
Please try doing
listPlans.removeAt(position)
notifyItemRemoved(position)
instead of
listPlans.removeAt(position)
notifyDataSetChanged()

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