Reputation: 3
I'm trying to create a calendar view for a reservation app. I need to show to the user which days are already in use.
For this i like to create a selector between continuous days like this:
For the calendar view i created a RecyclerView using java.util.Calendar as datasource, every day is a ViewHolder.
Adapter:
class CalendarAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var list = emptyArray<CalendarItem>()
override fun getItemViewType(position: Int): Int {
return list[position].viewType?.asInt ?: super.getItemViewType(position)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
ViewType.CURRENT_DAY.asInt -> {
val binding = ViewCurrentDayBinding.inflate(inflater, parent, false)
CurrentDayHolder(binding)
}
ViewType.DAY_OF_MONTH.asInt -> {
val binding = ViewDayOfMonthBinding.inflate(inflater, parent, false)
DayOfMonthHolder(binding)
}
ViewType.DAY_OF_WEEK.asInt -> {
val binding = ViewDayOfWeekBinding.inflate(inflater, parent, false)
DayOfWeekHolder(binding)
}
ViewType.SELECTED_DAY.asInt -> {
val binding = ViewSelectedDayBinding.inflate(inflater, parent, false)
SelectedDayHolder(binding)
}
ViewType.MOCK.asInt -> {
val binding = ViewMockDayBinding.inflate(inflater, parent, false)
MockDayHolder(binding)
}
else -> {
val binding = ViewMockDayBinding.inflate(inflater, parent, false)
MockDayHolder(binding)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getItemViewType(position)) {
ViewType.CURRENT_DAY.asInt -> {
(holder as CurrentDayHolder).bind(list[position], position, callback)
}
ViewType.DAY_OF_MONTH.asInt -> {
(holder as DayOfMonthHolder).bind(list[position], position, callback)
}
ViewType.DAY_OF_WEEK.asInt -> {
(holder as DayOfWeekHolder).bind(list[position])
}
ViewType.SELECTED_DAY.asInt -> {
(holder as SelectedDayHolder).bind(list[position], position, callback)
}
ViewType.MOCK.asInt -> {
(holder as MockDayHolder).bind(list[position])
}
}
}
override fun getItemCount(): Int {
return list.size
}
private val callback: (index: Int) -> Unit = {
// find current selected index and unselect
val currentSelectedIndex =
list.indices.find { el -> list[el].viewType == ViewType.SELECTED_DAY }
if (currentSelectedIndex != null) {
list[currentSelectedIndex].viewType = list[currentSelectedIndex].defaultViewType
notifyItemChanged(currentSelectedIndex)
}
// select the new index
list[it].viewType = ViewType.SELECTED_DAY
notifyItemChanged(it)
}
}
ViewHolder:
class CurrentDayHolder(var binding: ViewCurrentDayBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(
calendarItem: CalendarItem,
index: Int,
callback: (index: Int) -> Unit
) {
binding.day.text = calendarItem.label
binding.root.setOnClickListener {
callback.invoke( index)
}
}
}
The full project is avaible on GitHub
How can i archive my goal?
I also thought not to use a RecyclerView and create a custom view directly, with the obvious complexities of the case.
I'm sure there is a way to do this with the RecyclerView as well
Upvotes: 0
Views: 183
Reputation: 3
I solved the problem using your suggestions: I managed a state for each element of the recyclerview indicating the selection of start, destination and the intermediate value when I create the datasource of the elements. then in each viewholder I change the corresponding background.
Full solution available on the example code in the question.
Here a screen of the result.
thanks everyone for the help
Upvotes: 0
Reputation: 19544
With a RecyclerView you'd need to customise the item layout so you can display the different styles (normal item, start of selection, middle of selection, end of selection, all the stripey versions of those) and then calculate the state of each item in onBindViewHolder
so you can style it correctly, e.g. by having different background drawables you can switch between. But since these are all separate views, you might have trouble getting those stripes to line up correctly where one view ends and the adjacent one starts.
Also you could just use a GridLayout
or something for this - no need for a RecyclerView
when you're displaying all the items at once. You might want to consider a custom view with a grid/table where each item is a TextView
or a borderless Button
(better!), where you have another View
layer on top which is a custom view that draws the selections.
But since you'd have to draw the text as well (e.g. the white date over the orange highlight) you might find it easier to just make the whole thing as a custom view, where you're positioning all the text yourself. That's one of the benefits of custom views - you get more control over how it draws itself. You could always try subclassing an existing calendar widget! Use the work that's already been done
Upvotes: 1