I have a recyclerview that is being used to form a calendar. I have a custom flow that is used to populate 42 DayOfMonth
tiles, and an observer to submit it to the adapter.
My aim was to have tiles from the next and previous month shown in grey (these are sometimes shown in the first and last row, depending on where the first day of the month lies).
When the user clicks on a tile in the next/previous month the flow updates and the calendar updates to center on that month. The tiles should adjust to their new positions and be coloured accordingly. However, if a tile is grey when it is created it does not change its colour. I assume the if statement defining its colour is not rechecked.
I assume this is because the recyclerview is reusing the tiles and not recreating them. This makes sense and the DiffUtil callback isnt actioned because the items are the same items (just in a different adapter position).
AdapterActivitiesCalendar
class AdapterActivitiesCalendar(
private val listener: OnItemClickListener,
private val viewLifecycleOwner: LifecycleOwner,
private val dateItemSelected: LiveData<Long>
) : ListAdapter<DayOfMonthItem, AdapterActivitiesCalendar.DateItemViewHolder>(DiffCallItem()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DateItemViewHolder {
return DateItemViewHolder(RvItemCalendarDayOfMonthTileBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: DateItemViewHolder, position: Int) {
holder.bind(getItem(position))
}
inner class DateItemViewHolder(private var binding: RvItemCalendarDayOfMonthTileBinding) : RecyclerView.ViewHolder(binding.root) {
init {
binding.apply {
llItemHeader.setOnClickListener {
val position = adapterPosition
if (position != RecyclerView.NO_POSITION) {
val dayOfMonthItem = getItem(position)
listener.onClickDayOfMonthTile(dayOfMonthItem)
}
}
}
}
//Bind database information to item view
fun bind(item: DayOfMonthItem) {
binding.apply {
tvDate.text = item.dayOfMonth.toString()
@SuppressLint("ResourceAsColor")
if (((adapterPosition < 14) && (item.dayOfMonth > 14)) || ((adapterPosition > 28) && (item.dayOfMonth < 14))) {
tvDate.setTextColor(R.color.colourGrey)
ivIndicatorEvent.imageAlpha = 25
ivIndicatorActivity.imageAlpha = 25
} else{
//tvDate.setTextColor(R.color.colourBlack)
//ivIndicatorEvent.imageAlpha = 100
//ivIndicatorActivity.imageAlpha = 100
}
dateItemSelected.observe(viewLifecycleOwner) {
val position = adapterPosition
if (position != RecyclerView.NO_POSITION) {
val dayOfMonthItem = getItem(position)
//converts to day, rather than long to remove HH:mm:ss
if (convertLongToDate(dayOfMonthItem.timestamp) == convertLongToDate(dateItemSelected.value!!)) {
llItemHeader.setBackgroundResource(R.drawable.vec_border)
} else {
llItemHeader.setBackgroundResource(R.color.backgroundColour)
}
}
}
}
}
}
// Fragment Interface
interface OnItemClickListener {
fun onClickDayOfMonthTile(dayOfMonthItem: DayOfMonthItem)
fun onDragDayOfMonthTile(dayOfMonthItem: DayOfMonthItem)
fun onHoldDayOfMonthTile(dayOfMonthItem: DayOfMonthItem)
}
// update list when changes are made to the repository flow
class DiffCallItem : DiffUtil.ItemCallback<DayOfMonthItem>() {
override fun areItemsTheSame(oldItem: DayOfMonthItem, newItem: DayOfMonthItem) =
oldItem.timestamp == newItem.timestamp
override fun areContentsTheSame(oldItem: DayOfMonthItem, newItem: DayOfMonthItem) =
oldItem == newItem
}
I've been working on the recyclerview and with DiffUtil to try to get it to recheck the position when updated but have had no luck.
Any suggestions are appreciated.
Edit:
ViewModel List
val dayOfMonthList = dayOfMonthFlow.asLiveData()
Fragment Observer
viewModel.dayOfMonthList.observe(viewLifecycleOwner) {
calendarAdapter.submitList(it)
}
CodePudding user response:
I'm pretty sure your areContentsTheSame
needs to be improved. It will return true even when being moved to the next month, which means it won't make any changes to it. I'm not sure how to do it right and efficient but simply changing it to
override fun areContentsTheSame(oldItem: DayOfMonthItem, newItem: DayOfMonthItem) = false
should work.
Though you probably also need to comment this back in
//tvDate.setTextColor(R.color.colourBlack)
//ivIndicatorEvent.imageAlpha = 100
//ivIndicatorActivity.imageAlpha = 100
CodePudding user response:
According to your images, when you change the month (Next or Previous). You need to update whole list of the adapter and need to call notifyDatasetChanged(). i am sure you are calling that.
But i you look at the code in bind.
if (((adapterPosition < 14) && (item.dayOfMonth > 14)) || ((adapterPosition > 28) && (item.dayOfMonth < 14))) {
tvDate.setTextColor(R.color.colourGrey)
ivIndicatorEvent.imageAlpha = 25
ivIndicatorActivity.imageAlpha = 25
}
Above code changing alpha, means indicating this dates are not from the current month. you need to improve this conditions.
You need to check is from same month
fun isSameMonth(dayTime : Long, monthTime : Long) : Boolean
{
//Create 2 instances of Calendar
var cal1 = Calendar.getInstance();
var cal2 = Calendar.getInstance();
//set the given date in one of the instance and current date in the other
cal1.setTimeInMillis(dayTime );
cal2.setTimeInMillis(monthTime);
//now compare the dates using methods on Calendar
if(cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)) {
if(cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH)) {
return true
}
}
return false;
}
If you have date object or milliseconds in your item model,
if(isSameMonth(item.dayTime, currentSelectedMonthTime))
{
tvDate.setTextColor(R.color.colourGrey)
ivIndicatorEvent.imageAlpha = 25
ivIndicatorActivity.imageAlpha = 25
}