How to use select / deselect all items in Recyclerview Android - android

In my application I have one fragment and in this fragment I have one recyclerview and 2 button.
Button 1 is Select all and button 2 is Deselect all.
I write below codes for recyclerview:
`
private lateinit var binding: ItemDayMyBinding
private lateinit var context: Context
private val TYPE_HEADER = 0
private val TYPE_DAY = 1
private var firstDayDayOfWeek = 0
private var totalDays = 0
private var todayPosition = -1
private val addedDateList = mutableListOf<PersianDate>()
private val removedDateList = mutableListOf<PersianDate>()
private val selectedDays = mutableListOf<DayEntity>()
init {
firstDayDayOfWeek = items[0].dayOfWeek
totalDays = items.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
binding = ItemDayMyBinding.inflate(LayoutInflater.from(parent.context), parent, false)
context = parent.context
return ViewHolder()
}
override fun getItemCount() = 7 * 7
override fun getItemViewType(position: Int): Int {
return if (isPositionHeader(position)) {
TYPE_HEADER
} else {
TYPE_DAY
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(position)
holder.setIsRecyclable(false)
}
inner class ViewHolder : RecyclerView.ViewHolder(binding.root) {
//Bind data
fun bind(pos: Int) {
binding.apply {
var position = pos
position += 6 - (position % 7) * 2
//Check
if (totalDays < position - 6 - firstDayDayOfWeek) {
return
} else {
//Day
if (!isPositionHeader(position)) {
if (position - 7 - firstDayDayOfWeek >= 0) {
val day = items[position - 7 - firstDayDayOfWeek]
dayTxt.isVisible = true
//Day text options
dayTxt.text = day.num
dayTxt.setTextColor(ContextCompat.getColor(context, R.color.black))
//Holiday
if (day.isHoliday) {
dayTxt.setTextColor(ContextCompat.getColor(context, R.color.mystic))
} else {
dayTxt.setTextColor(ContextCompat.getColor(context, R.color.black))
//Today
if (day.isToday) {
todayPosition = position
}
//Previous of today
if (position < todayPosition) {
dayTxt.setTextColor(ContextCompat.getColor(context, R.color.spanishGray))
} else {
dayTxt.setTextColor(ContextCompat.getColor(context, R.color.black))
}
}
} else {
dayTxt.isVisible = false
}
} else {
//Header
dayTxt.text = NAME_OF_DAYS_OF_WEEK_NAME[position]
dayTxt.setTextColor(ContextCompat.getColor(context, R.color.weldonBlue))
}
}
}
}
}
private fun isPositionHeader(position: Int) = position < 7
I want when click on select all button, select all today and next days.
Not previous days!
Searched in stackoverflow and find this:
`
ArrayList<Item> selected = new ArrayList<Item>();
ArrayList<Item> items = new ArrayList<Item>();
public void selecteAll() {
selected.clear();
selected.addAll(items);
notifyDataSetChanged();
}
public void clearAll() {
selected.clear();
notifyDataSetChanged();
}
public void bindView() {
Item item = items.get(position);
if(selected.contains(item) {
// Do selected action
} else {
// Non selecetd ctions
}
}
`
In this solution just select all of items, but I want just select today and next days!
How can I it?

Not getting exactly what you have in Item and other data but yes below code can give you hint what you can do
I want when click on select all button, select all today and next days.
As per your requirement , you first need to determine that you need to perform selection operation or not and from where
So first you declare one boolean in your adapter class
private boolean needToSelectDays = false
after declaring it in adapter , you can make change in selectAll function like this
public void selecteAll() {
needToSelectDays = true
notifyDataSetChanged();
}
public void clearAll() {
needToSelectDays = false
notifyDataSetChanged();
}
in above code we are managing boolean variable only .. now it is time of use it and as per your code , you are binding data in ViewHolder class so you can add this code within your existing code
fun bind(pos: Int) {
// your existing code
if(needToSelectDays && dayGettingFromItem >= currentDay){
// you can write here logic of selecting date
}else{
// you can write here logic of unselecting dat
}
}
hope it will help you

Jacson
When you create adapter for recycler view then you pass list of model of items
so just create one variable like isSelected = false
on select all button click change all item model to selected is true and for deselect all button click for all item is selected is false
and if you need select only today and next days then set is selected true for only today and next day
after that notify dataset chenge so resule as you want

Related

How to set the background color of all ListView Items

I have got the following for setting the background color for a single item when clicking on it:
// Get the listView on the Home Fragment
val contactList = findViewById<ListView>(R.id.listViewContacts)
// Create IDs for each element
val id : Int = R.id.txtListElement
// Fill the ListView with the custom contact_list_template
arrayAdapter = ArrayAdapter(this,
R.layout.contact_list_template, id, contactSimple)
contactList.adapter = arrayAdapter
contactList.onItemClickListener = AdapterView.OnItemClickListener { adapterView, view,
position, _ ->
val selectedItem = adapterView.getItemAtPosition(position) as String
val itemIdAtPos = adapterView.getItemIdAtPosition(position)
view.setBackgroundColor(ContextCompat.getColor(this, R.color.purple_200))
...
}
But how can I set the color for every object inside the ListView?
I tried
contactList.setBackgroundColor(ContextCompat.getColor(this, R.color.white)) but that does not seem to work.
Try to define your custom BaseAdapter for the ListView. (I assume that the contact_list_template XML contains a TextView with id title).
class ListViewContactAdapter(
private val contactList: List<String>
) : BaseAdapter() {
private val lastClicked = ""
override fun getCount(): Int = contactList.size
override fun getItem(position: Int): String = contactList[position]
override fun getItemId(position: Int): Long = position.toLong()
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val view = convertView ?: LayoutInflater.from(parent.context).inflate(R.layout.contact_list_template, parent, false)
val holder = ViewHolderContact(view)
val contact = getItem(position)
holder.title.text = contact
if (contact == lastClicked) {
// Selected background
view.setBackgroundColor(ContextCompat.getColor(this, ...))
} else {
// Unselected background
view.setBackgroundColor(ContextCompat.getColor(this, ...))
}
view.setOnClickListener {
lastClicked = contact
notifyDataSetChanged()
}
return view
}
class ViewHolderContact(itemView: View) {
val title = itemView.findViewById<TextView>(R.id.title)
}
}
You can then set the adapter of the list with:
val contactList = findViewById<ListView>(R.id.listViewContacts)
contactList.adapter = ListViewContactAdapter(contactSimple)

RecyclerView showing two highlighted item

I have two scenario to highlight the item in recyclerview first one is when user click on any item so that clicked item should be highlighted and and second scenario is when last or recent item added into the list that time that last added item should be highlighted.
So my first scenario(onClick) is working fine but problem is in my second scenario so whenever I'm adding item into the list so two items are highlighted. Please suggest me where I am wrong. and one more thing that recent or last item should be perform auto click also. so How can I achieve it.
class CarItemAdapter(
private val activity: MainActivity,
private val carList: ArrayList<CarEntity>,
private val carViewModel: CarViewModel,
private val settings: Settings
) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var listener: OnItemsClickListener? = null
var rowIndex = 100
var listItemCount = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return when (viewType) {
VIEW_TYPE_ADDITEM -> ViewHolderAddCarItem(SettingsCarAddItemTileBinding.inflate(layoutInflater, parent, false))
VIEW_TYPE_ITEM -> ViewHolderCarItem(SettingsCarItemTileBinding.inflate(layoutInflater, parent, false))
else -> throw IllegalArgumentException("Invalid view type")
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ViewHolderCarItem -> holder.bind(carList[position -1], position-1)
is ViewHolderAddCarItem -> holder.bind()
}
}
inner class ViewHolderAddCarItem internal constructor(private var binding: SettingsCarAddItemTileBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind() {
binding.lifecycleOwner = activity
binding.handler = EventHandler()
binding.userSpecificCarSubtitle.setText(R.string.user_specific_car_subtitle)
}
inner class EventHandler {
fun onItemClicked() {
activity.showSubPage(Car360AddVehiclePage(activity), true)
}
}
}
inner class ViewHolderCarItem internal constructor(private var binding: SettingsCarItemTileBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(carEntity: CarEntity, position: Int) {
Timber.d("Data store position %s %s", position, carList.size)
listItemCount = itemCount
binding.lifecycleOwner = activity
val bmp = BitmapFactory.decodeByteArray(carEntity.image, 0, Objects.requireNonNull<ByteArray>(carEntity.image).size)
binding.carImage.setImageBitmap(bmp)
binding.carName.text = carEntity.label
binding.layoutCar.setOnClickListener {
listItemCount = 100
listener?.onItemClick(carEntity)
settings.finNumber = carEntity.finNumber
settings.selectedCarType = activity.getString(R.string.settings_category_view_sub_title_for_user_specific_car)
settings.isCarSelected = true
rowIndex = position
notifyDataSetChanged()
}
binding.deleteButton.setOnClickListener {
GlobalScope.launch(Dispatchers.IO) { carViewModel.deleteByFinNumber(carEntity.finNumber) }
carList.removeAt(position)
notifyDataSetChanged()
}
if(position == listItemCount-2){
binding.layoutCar.setBackgroundColor(activity.getColor(R.color.petrol))
binding.carName.setTextColor(activity.getColor(R.color.white))
}else{
binding.layoutCar.setBackgroundColor(activity.getColor(R.color.coolGray))
binding.carName.setTextColor(activity.getColor(R.color.coolGray_80k))
}
if (rowIndex == position) {
binding.layoutCar.setBackgroundColor(activity.getColor(R.color.petrol))
binding.carName.setTextColor(activity.getColor(R.color.white))
}
}
}
override fun getItemViewType(position: Int): Int {
return if (position == 0 && position <= carList.size) {
VIEW_TYPE_ADDITEM
} else VIEW_TYPE_ITEM
}
override fun getItemCount(): Int {
return carList.size + 1
}
interface OnItemsClickListener {
fun onItemClick(carEntity: CarEntity)
}
fun setWhenClickListener(listener: OnItemsClickListener) {
this.listener = listener
}
companion object {
const val VIEW_TYPE_ITEM = 0
const val VIEW_TYPE_ADDITEM = 1
}
}
MyFragmentSubPage : where I'm adding data into the list
if (isDialogVisible) {
Dialog imageDownloadCheckDialog = Dialog.createProgressBarDialog(getContext(), R.string.image_progressbar_title);
imageDownloadCheckDialog.setCancelable(false);
imageDownloadCheckDialog.show();
carViewModel.getGetDismissDialog().observe(getActivity(), isDismissDialog -> {
if (isDismissDialog) {
imageDownloadCheckDialog.dismiss();
carViewModel.dismissDialog(false);
new Thread(() -> {
CarEntity carEntity = carViewModel.getImageFor340Degree(vinNumber, "340");
// Here I am adding data into the list
recyclerImageList.add(carEntity);
}).start();
}
});
}
RecyclerView recyclerView = binding.getRoot().findViewById(R.id.list);
if (recyclerView != null) {
layoutManager = new GridLayoutManager(getContext(), 2);
recyclerView.setLayoutManager(layoutManager);
}
carItemAdapter = new CarItemAdapter(getActivity(), recyclerImageList, eventHandler, carViewModel, settings);
assert recyclerView != null;
recyclerView.setAdapter(carItemAdapter);
recyclerView.getRecycledViewPool().setMaxRecycledViews(carItemAdapter.VIEW_TYPE_ITEM, 50);
First make a CarEntity var inside your adapter called selectedCar.
Make a function inside your adapter called submitItem
fun submitItem(car: CarEntity) {
selectedCar = car //set the selected car to the new car
notifyItemChanged(rowIndex)//rebind old selected car
carList.add(car) //add new car to list
rowIndex = carList.size() - 1 //set current selected index
notifyItemInserted(rowIndex)//notify the change at selected index
}
And your dialog
new Thread(() -> {
CarEntity carEntity = carViewModel.getImageFor340Degree(vinNumber, "340");
// Here I am adding data into the list
requireActivity().runOnUiThread{
carItemAdapter.submitItem(carEntity);
}
}).start();
and your button
binding.layoutCar.setOnClickListener {
listItemCount = 100
listener?.onItemClick(carEntity)
settings.finNumber = carEntity.finNumber
settings.selectedCarType = activity.getString(R.string.settings_category_view_sub_title_for_user_specific_car)
selectedCar = carEntity //set the select car on click
notifyItemChanged(rowIndex) //notify old row has changed
notifyItemChanged(position) //notify new row has changed
rowIndex = position // set current selected row
}
and on bind to set selected view stuff
if(carEntity == selectedCar){
//highlight row
}

Initializing on item selected listener (Custom Spinner Adapter)

I have created a custom spinner adapter which returns two views (item_new_area & item_area), item_new_area is static with an option Add New and item_area populated dynamic.
When Add new is selected,I want to launch the dialog for add new area but i keep on getting an error that lateinit property listener has not been initialized
Custom Spinner Adapter
class AreaSpinnerAdapter(private val inflater: LayoutInflater) : BaseAdapter() {
var areas: List<AreaEntity>? = null
lateinit var listener: OnItemSelectedListener
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
return when (getItemViewType(position)) {
1 -> {
val binding: ItemNewAreaBinding = DataBindingUtil.inflate<ItemNewAreaBinding>(
inflater,
R.layout.item_new_area,
parent, false
).also {
it.root.tag = it
}
binding.addNew.setOnClickListener {
listener.onClick()
}
binding.root
}
else -> {
val binding: ItemAreaBinding = DataBindingUtil.inflate<ItemAreaBinding>(
inflater,
R.layout.item_area,
parent, false
).also {
it.root.tag = it
}
binding.area = getItem(position)
binding.root
}
}
}
override fun getItemViewType(position: Int): Int {
return if (position == 1) {
1
} else {
2
}
}
fun setOnItemClickListener(listener: OnItemSelectedListener) {
this.listener = listener
}
interface OnItemSelectedListener {
fun onClick()
}
}
I try to set the on Item Clicked listener in the onCreateView method of my fragment:
val spinnerAdapter = AreaSpinnerAdapter(layoutInflater)
spinnerAdapter.setOnItemClickListener(object :
AreaSpinnerAdapter.OnItemSelectedListener {
override fun onClick() {
addNewAreaDialog()
}
})
binding?.spinnerArea?.adapter = spinnerAdapter
Error Log
kotlin.UninitializedPropertyAccessException: lateinit property listener has not been initialized
at com.test.adapter.AreaSpinnerAdapter.getListener(AreaSpinnerAdapter.kt:15)
at com.test.adapter.AreaSpinnerAdapter$getView$1.onClick(AreaSpinnerAdapter.kt:50)
line 15 = lateinit var listener: OnItemSelectedListener
line 25 = listener.onClick()
I know that am supposed to initialize the listener before i call getView method but for some reasons am failing to initialize the listener, Any help on how to do it shall be be greatly appreciated. Thanks
You need to move the listener AreaSpinnerAdapter to the constructor.
class AreaSpinnerAdapter(
private val inflater: LayoutInflater,
private val listener: OnItemSelectedListener
) : BaseAdapter() {
var areas: List<AreaEntity>? = null
and then call it like
val spinnerAdapter = AreaSpinnerAdapter(layoutInflater,
object: AreaSpinnerAdapter.OnItemSelectedListener {
override fun onClick() {
addNewAreaDialog()
}
})

How to set String key in recyclerview-selection in Android?

I am implementing RecyclerView and want to get details of selected items.
I am following a tutorial given here. But I want to get some string key instead of item position.
For example, if i do like this:
for (selection in selectionTracker?.selection!!)
Log.e("tag", selection.toString())
I get indexes as output, but I want to get string keys here.
But I don't know what further changes should be made if I do like this:
fun getItemDetails() = object: ItemDetailsLookup.ItemDetails<Long>() {
override fun getSelectionKey() = itemId
override fun getPosition() = adapterPosition
}
and change it into:
fun getItemDetails() = object: ItemDetailsLookup.ItemDetails<Long>() {
override fun getSelectionKey() = myData[adapterPosition].myKey // a string
override fun getPosition() = adapterPosition
}
----EDIT----
These are my codes:
The adapter class:
class AdapterSubjectsIndexRV (
private val indexList: ArrayList<ModelSubjectsIndex>,
val itemClick: (String) -> Unit
): RecyclerView.Adapter<AdapterSubjectsIndexRV.MyViewHolder>() {
// every recycler view item has unique id
init {
setHasStableIds(true)
}
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder.
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val courseValue = itemView.findViewById<TextView>(R.id.course_value)!!
val courseCode = itemView.findViewById<TextView>(R.id.course_code)!!
val courseName = itemView.findViewById<TextView>(R.id.course_name)!!
val progressValue = itemView.findViewById<TextView>(R.id.progress_value)!!
val progressBar = itemView.findViewById<ProgressBar>(R.id.progress_bar)!!
val selectedOverlay = itemView.findViewById<View>(R.id.subjects_card_view_selected)!!
// set click events
init {
itemView.setOnClickListener {
itemClick(courseCode.text.toString())
}
}
// get item details
fun getItemDetails() = object: ItemDetailsLookup.ItemDetails<String>() {
// override fun getSelectionKey() = itemId
override fun getSelectionKey() = indexList[adapterPosition].courseCode
override fun getPosition() = adapterPosition
}
}
// lookup class
class MyItemDetailsLookup(private val rv: RecyclerView): ItemDetailsLookup<String>() {
override fun getItemDetails(e: MotionEvent): ItemDetails<String>? {
val v = rv.findChildViewUnder(e.x, e.y)
return if (v != null) {
(rv.getChildViewHolder(v) as MyViewHolder).getItemDetails()
} else null
}
}
// set tracker
var tracker: SelectionTracker<Long>? = null
// Create new views (invoked by the layout manager)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AdapterSubjectsIndexRV.MyViewHolder {
// create a new view
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.subjects_card, parent, false)
return MyViewHolder(itemView)
}
// Replace the contents of a view (invoked by the layout manager)
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.courseValue.text = indexList[position].courseValue
holder.courseCode.text = indexList[position].courseCode
holder.courseName.text = indexList[position].courseName
holder.progressValue.text = "${indexList[position].progress}%"
holder.progressBar.progress = indexList[position].progress
// Highlight the item if it's selected
holder.selectedOverlay.visibility = if (tracker!!.isSelected(position.toLong())) View.VISIBLE else View.INVISIBLE
}
// Return the size of your data set (invoked by the layout manager)
override fun getItemCount() = indexList.size
override fun getItemId(position: Int): Long {
return position.toLong()
}
}
And this is how I am setting my fragment:
val rvAdapter = AdapterSubjectsIndexRV(getSubjectsIndexList(subjectsList)) {
// if not in selection mode
if (selectionTracker?.selection?.size() == 0) {
listener.showSubjectDetails(it)
}
}
// set recycler view
val recyclerView = theView.findViewById<RecyclerView>(R.id.subjects_recycler_view)
recyclerView.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(activity)
adapter = rvAdapter
}
// set selection tracker
selectionTracker = SelectionTracker
.Builder<Long>(
"subjects_index_selection",
recyclerView,
StableIdKeyProvider(recyclerView),
AdapterSubjectsIndexRV.MyItemDetailsLookup(recyclerView),
StorageStrategy.createLongStorage()
).withSelectionPredicate(
SelectionPredicates.createSelectAnything()
).build()
rvAdapter.tracker = selectionTracker
if(savedInstanceState != null)
selectionTracker?.onRestoreInstanceState(savedInstanceState)
return theView
Change the generic type from <Long> to <String>, then you will be able to return a string key.
fun getItemDetails() = object: ItemDetailsLookup.ItemDetails<String>() {
override fun getSelectionKey() = myData[adapterPosition].myKey
// ...
}
I am a little bit confused by your code, but the best way to get the details is to do it on the `onBindViewHolder method. Call your on click method and then within that do your method to get the string.

How to select all items which listed in recyclerview?

I have list of items inside of recyclerview, and they are multiple selectable.
I want to have select button to select all, and if selected deselect all. I didn't see any option to iterate through RecyclerView.Adapter to do that. How can I implement that?
Thank you.
Try to maintain Selected item list and list of items in Adapter,
When you select "Select All" button, just add all item in selected item list and call notifyDataSetChanged
Just a sudo code
class adapter {
ArrayList<Item> selected = new ArrayList<Item>();
ArrayList<Item> items = new ArrayList<Item>();
public void selecteAll() {
selected.clear();
selected.addAll(items);
notifyDataSetChanged();
}
public void clearAll() {
selected.clear();
notifyDataSetChanged();
}
public void bindView() {
Item item = items.get(position);
if(selected.contains(item) {
// Do selected action
} else {
// Non selecetd ctions
}
}
}
In my case,
Step - 1 :- first globally initialize flag in activity class
boolean flagSelectAll = false;
Step - 2 :- Then set below code in Onclick of Button
#OnClick(R.id.btnSelectAll)
public void setBtnSelectAll(View view){
for (int i=0;i<dMyItemLists.size();i++){
if (dMyItemLists.get(i).isSelected()){
flagSelectAll = true;
dMyItemListAdapter.selectAllItem(false);
dMyItemListAdapter.notifyDataSetChanged();
}
else {
dMyItemListAdapter.selectAllItem(true);
dMyItemListAdapter.notifyDataSetChanged();
}
break;
}
}
Step - 3 :- Then create method selectAllItem in recyclerview's adpater class like below :
public void selectAllItem(boolean isSelectedAll) {
try {
if (itemList != null) {
for (int index = 0; index < itemList.size(); index++) {
itemList.get(index).setSelected(isSelectedAll);
}
}
notifyDataSetChanged();
} catch (Exception e) {
e.printStackTrace();
}
}
There is no need to maintain another list with selected items. You need to create a method to set a flag and in the onBindViewHolder method just check for that flag. Something like this:
class MyAdapter(val context: Context): RecyclerView.Adapter<MyAdapter.ViewHolder>() {
val mItems = mutableListOf<ItemObject>()
private var selectAllItems: Boolean = false
fun updateList(list: List<ItemObject>) {
mItems.addAll(list)
notifyDataSetChanged()
}
fun selectAllItems(selectAll: Boolean){
selectAllItems = selectAll
notifyDataSetChanged()
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
val pos = viewHolder.adapterPosition
viewHolder.checkbox.isSelected = selectAllItems
}
class ViewHolder(val view: View): RecyclerView.ViewHolder(view) {
val checkbox: Checkbox = view.myCheckBox
}
}
Maintaining a separate list or flag doesn't work for me, as the contents of my recycler view can change at any given moment, and calling notifyDataSetChanged() can be costly for large data sets. The solution I came up with doesn't use either, instead iterating over the recycler views children. For example, inside whatever function responds to your "select all action", iterate the child views like so:
RecyclerView rv = ...;
List<View> selection = ...;
...
public void selectAll() {
int count = rv.getChildCount();
for (int i = 0; i < count; i++) {
View view = rv.getChildAt(i);
if (!view.isActivated()) {
view.setActivated(true);
selection.add(view);
}
}
}

Categories

Resources