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
Related
In my application I want use checkbox in recyclerview.
I want when users click on item check/unchecked checkbox and when click on Select All or clear, checked/unchecked all of items.
I write below codes, when click on select all checked all of checkboxes but after click on one of checkbox not unchecked!
I should click twice on checkbox after unchecked!
My UI is :
My Adapter codes :
class DataListAdapter #Inject constructor() : RecyclerView.Adapter<DataListAdapter.ViewHolder>() {
private lateinit var binding: ItemWithCheckboxBinding
private lateinit var context: Context
private var moviesList = emptyList<String>()
private var isSelectedAll = false
private var checkBoxState = SparseBooleanArray()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
binding = ItemWithCheckboxBinding.inflate(LayoutInflater.from(parent.context), parent, false)
context = parent.context
return ViewHolder()
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(moviesList[position])
holder.checkBox.isChecked = checkBoxState.get(position, false)
var state: String
if (!isSelectedAll) {
holder.checkBox.isChecked = false
state = LIST_STATE_REMOVE
onItemClickListener?.let { it(moviesList[position], state) }
} else {
holder.checkBox.isChecked = true
state = LIST_STATE_ADD
onItemClickListener?.let { it(moviesList[position], state) }
}
}
override fun getItemCount() = moviesList.size
inner class ViewHolder : RecyclerView.ViewHolder(binding.root) {
val checkBox = binding.itemCheck
#SuppressLint("SetTextI18n")
fun bind(item: String) {
binding.apply {
//Views
itemTitle.text = item
//Click
var state: String
binding.root.setOnClickListener {
if (!checkBoxState.get(adapterPosition, false)) {
checkBox.isChecked = true
checkBoxState.put(adapterPosition, true)
state = LIST_STATE_ADD
} else {
checkBox.isChecked = false
checkBoxState.put(adapterPosition, false)
state = LIST_STATE_REMOVE
}
onItemClickListener?.let { it(item, state) }
}
}
}
}
#SuppressLint("NotifyDataSetChanged")
fun selectAll() {
isSelectedAll = true
notifyDataSetChanged()
}
#SuppressLint("NotifyDataSetChanged")
fun unSelectAll() {
isSelectedAll = false
notifyDataSetChanged()
}
private var onItemClickListener: ((String, String) -> Unit)? = null
fun setOnItemClickListener(listener: (String, String) -> Unit) {
onItemClickListener = listener
}
fun setData(data: List<String>) {
val moviesDiffUtil = NotesDiffUtils(moviesList, data)
val diffUtils = DiffUtil.calculateDiff(moviesDiffUtil)
moviesList = data
diffUtils.dispatchUpdatesTo(this)
}
class NotesDiffUtils(private val oldItem: List<String>, private val newItem: List<String>) : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldItem.size
}
override fun getNewListSize(): Int {
return newItem.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldItem[oldItemPosition] === newItem[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldItem[oldItemPosition] === newItem[newItemPosition]
}
}
}
For control select all and clear checkboxes I write codes in Fragment:
selectAllTxt.setOnClickListener { dataListAdapter.selectAll() }
clearTxt.setOnClickListener { dataListAdapter.unSelectAll() }
How can I fix it?
Try to use the !checkBox.isChecked instead of !checkBoxState.get(adapterPosition, false) when clicking:
binding.root.setOnClickListener {
if (!checkBox.isChecked) {
checkBox.isChecked = true
checkBoxState.put(adapterPosition, true)
state = LIST_STATE_ADD
} else {
checkBox.isChecked = false
checkBoxState.put(adapterPosition, false)
state = LIST_STATE_REMOVE
}
onItemClickListener?.let { it(item, state) }
}
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)
}
}
}
I am having an issue with recyclerview.adapter what i am doing is
Passing a list of objects to a dialog where dialog have a recyclerview and two buttons.
1. Cancel and 2. Done
If user selects items from recyclerview that will be passed to fragment.
Now assume if user have 2 items already selected, and opens dialog for more and selected some items but now user wants to cancel the selection and go back(without unselecting the ites) to fragment with old data but currently it is updating the list of object of fragment.
Here is the code of dialog.
fun showChooserDialog(
baseActivity: AppCompatActivity,
typeCode: Int,
dataList: ArrayList<PrefsStateModel>
) {
var alertDialog: AlertDialog? = null
val dialogBuilder =
AlertDialog.Builder(baseActivity, R.style.CustomDialog)
dialogBuilder.setCancelable(true)
val inflater = LayoutInflater.from(baseActivity)
val dialogView: View = inflater.inflate(R.layout.view_select_prefs, null)
dialogBuilder.setView(dialogView)
val btnCancel = dialogView.findViewById<MaterialButton>(R.id.btnCancel)
val btnDone = dialogView.findViewById<MaterialButton>(R.id.btnDone)
val recyclerView = dialogView.findViewById<RecyclerView>(R.id.listViewPrefs)
recyclerView.setHasFixedSize(true)
val layoutManager = LinearLayoutManager(baseActivity)
recyclerView.layoutManager = layoutManager
val adapter =
SelectPrefAdapter(
baseActivity,
dataList
)
recyclerView.adapter = adapter
btnCancel.setOnClickListener {
preferenceView.onUserNotSelected()
alertDialog?.dismiss()
}
btnDone.setOnClickListener {
val data = adapter.getList()
preferenceView.onUserSelectedPrefDone(data, typeCode)
alertDialog?.dismiss()
}
alertDialog = dialogBuilder.create()
alertDialog.window?.setLayout(600, 400)
alertDialog.show()
}
What i wants to do when user clicks on cancel button even he selected the more items from recyclerview, then the fragment's recyclerview should not update the items. it must be showing the old items. but currently it is updating the items even i am not passing when clicking on cancel button.
Here is adapter's code.
class SelectPrefAdapter(var context: Context, var dataList: ArrayList<PrefsStateModel>) :
RecyclerView.Adapter<SelectPrefAdapter.UserViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, p1: Int) =
UserViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.view_pref_adapter, parent, false)
)
override fun getItemCount() = dataList.size
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
var prefsState = dataList[position]
holder.lable.text = prefsState.name
if (prefsState.isSelected) {
holder.lable.setTextColor(context.resources.getColor(R.color.colorPrimary))
holder.icon.visibility = View.VISIBLE
} else {
holder.lable.setTextColor(context.resources.getColor(R.color.colorGreyShade2))
holder.icon.visibility = View.INVISIBLE
}
holder.lable.setOnClickListener {
if (prefsState.isSelected) {
prefsState.isSelected = false
holder.lable.setTextColor(context.resources.getColor(R.color.colorPrimary))
holder.icon.visibility = View.VISIBLE
} else {
prefsState.isSelected = true
holder.lable.setTextColor(context.resources.getColor(R.color.colorGreyShade2))
holder.icon.visibility = View.INVISIBLE
}
notifyDataSetChanged()
}
}
class UserViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val icon = view.imageViewIcon
val lable = view.textviewPrefName
}
fun getList(): ArrayList<PrefsStateModel> {
return dataList
}
}
This is main adapter code from where i pass the data to dialog's recyclerview adapter.
class ProfilePrefAdapter(
var context: Context,
var dataListPref: ArrayList<PrefsStateModel>,
var onPrefSelection: OnPrefSelection
) :
RecyclerView.Adapter<ProfilePrefAdapter.PrefViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, p1: Int) = PrefViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.view_pref, parent, false)
)
override fun getItemCount() = dataListPref.size
override fun getItemViewType(position: Int): Int {
return super.getItemViewType(position)
}
override fun onBindViewHolder(holder: PrefViewHolder, position: Int) {
val prefs = dataListPref[position]
holder.chipView.text = prefs.name
if (prefs.isSelected) {
holder.chipView.setBackgroundResource(R.drawable.rounded_border_selected)
holder.chipView.setTextColor(context.resources.getColor(R.color.colorWhite))
} else {
holder.chipView.setBackgroundResource(R.drawable.rounded_border_unselected)
holder.chipView.setTextColor(context.resources.getColor(R.color.colorGreyShade1))
}
if (position == dataListPref.size - 1) {
onPrefSelection.onPrefSelection(getSelected())
}
holder.chipView.setOnClickListener {
if (prefs.isSelected) {
prefs.isSelected = false
holder.chipView.setBackgroundResource(R.drawable.rounded_border_unselected)
holder.chipView.setTextColor(context.resources.getColor(R.color.colorGreyShade1))
} else {
prefs.isSelected = true
holder.chipView.setBackgroundResource(R.drawable.rounded_border_selected)
holder.chipView.setTextColor(context.resources.getColor(R.color.colorWhite))
}
notifyDataSetChanged()
onPrefSelection.onPrefSelection(getSelected())
}
}
class PrefViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val chipView = view.textViewPref
}
fun setList(data: ArrayList<PrefsStateModel>) {
clearData()
dataListPref.addAll(data)
notifyDataSetChanged()
}
private fun clearData() {
dataListPref.clear()
}
fun getData(): List<PrefsStateModel> {
return dataListPref.toMutableList()
}
private fun getSelected(): Int {
var count = 0
for (data in dataListPref) {
if (data.isSelected) {
count++
}
}
return count
}
}
This is where i am getting data and passing to dialog.
private fun showDialog(type: Int) {
var dataToPasses: ArrayList<PrefsStateModel>? = ArrayList()
when (type) {
1 -> {
dataToPasses?.clear()
dataToPasses?.addAll(adapterGener!!.getData())
}
2 -> {
dataToPasses?.clear()
dataToPasses?.addAll(adapterContent!!.getData())
}
3 -> {
dataToPasses?.clear()
dataToPasses?.addAll(adapterLanuage!!.getData())
}
}
utils.showLog(TAG, "data to be sent to dialog $dataToPasses and $generPrefList")
preferencePresenter.showChooserDialog(baseActivity, type, dataToPasses!!)
}
It's happing because your fragment and recyclreview are working on the same object list its called shallow copy. time both has the latest copies of data. to solve this problem you need to create deep copy and pass that to the adapter.
Example
ArrayList<PrefsStateModel> newList = new ArrayList<PrefsStateModel>();
for(PrefsStateModel p : oldList) {
newList.add(p.clone());
}
public class PrefsStateModel{
String s;
Date d;
...
public PrefsStateModel clone(){
PrefsStateModel p = new PrefsStateModel();
p.s = this.s.clone();
p.d = this.d.clone();
...
return p;
}
}
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
}
}
}
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