I am really noob into kotlin and I was trying to implement applandeo calendar library o my project in kotlin. Everything works well if you use activities but when changing into fragments I don't know how to give context because "this" is not working as a Context. In te function openDatePicker() the first parameter should be the context, but no idea about how to get it.
Also I don't know if its possible to pass from a fragment to an activity. My project is structured as a main activity with a bottom navigation bar where every elements redirects to the fragment. This code is inside one of those fragments. Any help or idea will be great ! :)
class CalendarFragment : Fragment(), OnDayClickListener, OnSelectDateListener{
private lateinit var binding: CalendarViewFragmentBinding
private val notes = mutableMapOf<EventDay, String>()
private lateinit var appContext: Context
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
val context = this.context
// Inflate the layout for this fragment
val calendar_view = inflater.inflate(R.layout.calendar_view_fragment, container, false)
binding = CalendarViewFragmentBinding.inflate(layoutInflater)
binding.fabButton.setOnClickListener { openDatePicker() }
binding.calendarView.setOnDayClickListener(this)
return calendar_view
}
private fun openDatePicker() {
DatePickerBuilder(************, this)
.pickerType(CalendarView.ONE_DAY_PICKER)
.headerColor(R.color.md_theme_light_primary)
.todayLabelColor(R.color.md_theme_light_primary)
.selectionColor(R.color.md_theme_light_secondary)
.dialogButtonsColor(R.color.md_theme_light_secondary)
.build()
.show()
}
I tried functions such as requireContext(), requireActivity(), requireContext().applicationContext, this.context, but no one working as I expect.
CodePudding user response:
Just use it from Fragment. Here is the documentation
private fun openDatePicker() {
DatePickerBuilder(requireContext(), this)
.pickerType(CalendarView.ONE_DAY_PICKER)
.headerColor(R.color.md_theme_light_primary)
.todayLabelColor(R.color.md_theme_light_primary)
.selectionColor(R.color.md_theme_light_secondary)
.dialogButtonsColor(R.color.md_theme_light_secondary)
.build()
.show()
}
CodePudding user response:
Your Fragment
is attached to its context when its onAttach()
callback is invoked. This happens before it reaches the CREATED
lifecycle state:
When your fragment reaches the
CREATED
state, it has been added to aFragmentManager
and theonAttach()
method has already been called.
What this means is that by the time onCreate
is called (or any lifecycle callbacks after that, including onCreateView
, onViewCreated
, onStart
etc.) your fragment will have a context, and you can access it using requireContext()
.
You could also use getContext()
, which you can access as a property with context
, but that returns null if the Fragment
isn't associated with a context yet - meaning you have to null-check and handle that possibility. requireContext()
will throw an exception if you don't have that context yet, but if you're making sure to only call it when the fragment is in the CREATED
state (or later) then it will be safe, and you won't need to check the return value. This is the recommended way of doing things.
So as long as you're accessing the Context
in a lifecycle callback like onCreateView
, requireContext()
will work - that's how you get the Fragment
's context. Because you're calling openDatePicker
from inside onCreateView
, you can just use requireContext()
in that function - it's safe at that point!
But you can't define it as a normal top-level variable like this:
class MyFragment : Fragment {
var appContext: Context = requireContext()
}
because that variable is assigned at construction time, which happens way before the fragment reaches the CREATED
state. You don't have access to the context at construction time, so any top-level stuff that requires it will end up throwing an exception. This goes for Activities too! That's why you have to assign stuff later, like in onCreate
(and this is where lateinit
comes in useful, you can have a top-level variable without having to assign it before you're ready)
And no, you can't pass this
as a Context
when you're in a Fragment
, because a Fragment
isn't a Context
. It works for an Activity
because that is a Context
- to be more accurate it's able to provide one, but that only works after it's in the CREATED
state. That's why you can get in trouble using it in top-level declarations like this
class MyActivity : AppCompatActivity {
val thing = ThingThatRequiresContext(this)
}
because like we just talked about, at construction time the Activity
doesn't have access to a context, and the thing that requires it (if it tries to access it immediately) will end up throwing an exception. You see that error a lot, where an Activity
can't be started because something like this is happening during initialisation.