Issue
I'm pretty sure this is impossible, but I just wanted to check before I use a 3rd party library. The DatePicker doesn't really let you customize the UI, and it's basically a full screen DialogFragment so putting something above or below (or even on top) doesn't seem to be an option.
My calendar currently looks like this:
But I would like it to look like this design doc with a footer at the bottom. Let me know if you have any ideas, because I'm out of them. Thanks!
val builder = MaterialDatePicker.Builder.dateRangePicker()
.setTheme(R.style.ThemeOverlay_MaterialComponents_MaterialCalendar_Fullscreen)
.setCalendarConstraints(
CalendarConstraints.Builder()
.setStart(Date().time)
.setEnd(oneYearFrom().time)
.setValidator(AvailabilityValidator(it.unavailableDays)).build()
)
builder.setInputMode(MaterialDatePicker.INPUT_MODE_CALENDAR)
builder.build().show(requireActivity().supportFragmentManager, TAG_DATE_RANGE_PICKER)
Solution
The only way to add a footer to the MaterialDateRangePicker
is to get access to the Picker root View and from there you can find the Picker FrameLayout Container
(with Id app:id/mtrl_calendar_frame
) and get access to its child View the Vertical LinearLayout Container
which contains of three children: (a Days-GridView
, a Separator-View
and a RecyclerView
) in a vertical orientation. What you want is to add a footer below the RecyclerView. Because the parent is a LinearLayout you can add the weight
attribute to all its children to be weight=0
except the RecyclerView
(which render the months) to be weight=1
to get all the remaining space.
To access the Picker root View you have to observe the MaterialDatePicker
(DialogFragment) lifecycle and add the footer View in onStart()
of DialogFragment. For this purpose you can use the DefaultLifecycleObserver
.
Below is full working example to add a footer View:
private fun showRangeDatePicker() {
//initialize dateRangePicker
val builder = MaterialDatePicker.Builder.dateRangePicker()
builder.setTheme(R.style.ThemeOverlay_MaterialComponents_MaterialCalendar_Fullscreen)
//set CalendarConstraints
val constraintsBuilder = CalendarConstraints.Builder()
val c: Calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
constraintsBuilder.setStart(c.getTimeInMillis())
c.add(Calendar.YEAR, 1)
constraintsBuilder.setEnd(c.getTimeInMillis())
builder.setCalendarConstraints(constraintsBuilder.build())
//set InputMode and Title
builder.setInputMode(MaterialDatePicker.INPUT_MODE_CALENDAR)
builder.setTitleText("Pick dates")
//get MaterialDatePicker instance and set a lifecycle Observer to listen for DialogFragment lifecycle
//DefaultLifecycleObserver needs androidx.appcompat:appcompat:1.4.0 and add the FooterView in onStart of DialogFragment
val picker: MaterialDatePicker<*> = builder.build()
picker.lifecycle.addObserver(object : DefaultLifecycleObserver
{
override fun onCreate(owner: LifecycleOwner) {
}
override fun onStart(owner: LifecycleOwner) {
//add footerView in onStart of DialogFragment so you can access the picker.view
addMyFooterView(picker)
}
override fun onResume(owner: LifecycleOwner) {
}
override fun onDestroy(owner: LifecycleOwner) {
//remove Lifecycle Observer
picker.lifecycle.removeObserver(this)
}
})
picker.show(supportFragmentManager, picker.toString())
}
private fun addMyFooterView(picker: MaterialDatePicker<*>) {
//get the MaterialDatePicker root View
val rootView = picker.view as LinearLayout
//find the Picker FrameLayout Container with ID: app:id/mtrl_calendar_frame
val frameLayout = rootView.findViewById<FrameLayout>(resources.getIdentifier("mtrl_calendar_frame", "id", packageName))
//and get the Picker LinearLayout Container (which contains of 3 children: Days-GridView, Separator-View and a RecyclerView)
val linearContainer = frameLayout.getChildAt(0) as LinearLayout
//for each child in Vertical LinearLayout Container set the weight to RecyclerView to 1 and 0 to all others.
//(This is necessary before adding the footerView to distribute correctly each children Height)
for (i in 0 until linearContainer.childCount) {
val v = linearContainer.getChildAt(i)
val params = v.layoutParams as LinearLayout.LayoutParams
params.weight = if (v is RecyclerView) 1f else 0f
v.layoutParams = params
}
//and finally add the 4th child in the above linearContainer to be the FooterView
val myFooterView = layoutInflater.inflate(R.layout.footer_layout, null)
val myFooterViewHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50f, resources.displayMetrics).toInt()
myFooterView.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, myFooterViewHeight, 0f)
myFooterView.setBackgroundColor(Color.WHITE)
linearContainer.addView(myFooterView)
//set any FooterView click listeners
myFooterView.findViewById<View>(R.id.textView2).setOnClickListener {}
}
Where footer_layout
is your custom footer xml layout like below:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp">
<View
android:id="@+id/separatorView"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentTop="true"
android:background="@android:color/darker_gray"/>
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:textStyle="bold"
android:layout_marginEnd="10dp"
android:layout_centerVertical="true"
android:layout_alignParentStart="true"
android:text="Match exact dates" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:textStyle="normal"
android:layout_marginEnd="10dp"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/textView1"
android:text="±1 day" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:textStyle="normal"
android:layout_marginEnd="10dp"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/textView2"
android:text="±3 days" />
</RelativeLayout>
Result:
Answered By - MariosP