Android Date Picker dialog context parameter - android

I am playing around with android and I wanted to create a date picker in my app. I followed the tutorial at developer.android.com which shows the following code:
class DatePickerFragment : DialogFragment(), DatePickerDialog.OnDateSetListener {
override fun onCreateDialog(savedInstanceState: Bundle): Dialog {
// Use the current date as the default date in the picker
val c = Calendar.getInstance()
val year = c.get(Calendar.YEAR)
val month = c.get(Calendar.MONTH)
val day = c.get(Calendar.DAY_OF_MONTH)
// Create a new instance of DatePickerDialog and return it
return DatePickerDialog(activity, this, year, month, day)
}
override fun onDateSet(view: DatePicker, year: Int, month: Int, day: Int) {
// Do something with the date chosen by the user
}
}
However, when I use this code android studio complains that there is a Type mismatch on the first parameter of DatePickerDialog:
Type mismatch.
Required: Contex
Found: FragmentActivity?
I also tried to get the context as activity?.applicationContext but that does not solve it either.

Is because FragmentActivity? is nullable ? so you need a non-nullable Context. You can use:
requireContext()
The function requireContext() is the recommended way of getting the context for non-nullable in Kotlin, in fragments. It doesn't mean is not nullable, the Context can always end up as null. What the function does is to throw an exception if is null, that is why you can use it as if weren't null. The exception is mostly never thrown and if it happens is because it was used before or after the Context were available, so using context!! would have also crashed.
Using activity!!.applicationContext would also work, but that is a cast to not nullable from a nullable and therefore not recommended.
This is very similar to requireActivity()
The last thing you could try is using this which would be the Fragment, sadly you can't
val context: Context = this
That would be not null because the fragment is not null if is doing something, but the fragment doesn't extend from Context like activities do (if you follow the inheritance of the context for the activities the 3 more upper parents are ContextThemeWrapper, ContextWrapper and Context)

Related

How to give context to fragment in Kotlin?

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.
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 a FragmentManager and the onAttach() 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.
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()
}
Hi fragment has the context. You just have to use context instead of this, and if you want a context that is never null, use requireContext()

lateinit property action has not been initialized in kotlin, how to fix it?

import android.app.Dialog
import android.app.TimePickerDialog
import android.os.Bundle
import android.text.format.DateFormat
import android.widget.TimePicker
import androidx.fragment.app.DialogFragment
import java.util.*
class TimePickerFragment : DialogFragment(), TimePickerDialog.OnTimeSetListener {
lateinit var action : (String) -> Unit
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// Use the current time as the default values for the picker
val c = Calendar.getInstance()
val hour = c.get(Calendar.HOUR_OF_DAY)
val minute = c.get(Calendar.MINUTE)
// Create a new instance of TimePickerDialog and return it
return TimePickerDialog(activity, this, hour, minute, DateFormat.is24HourFormat(activity))
}
override fun onTimeSet(view: TimePicker, hourOfDay: Int, minute: Int) {
var min = minute.toString()
if(min.toInt() < 10) min = "0$min"
action("$hourOfDay:$min")
}
fun setListener(action: (String) -> Unit) {
this.action = action
}
}
my declare action was detected error in firebase inside fun setlistener this.action=action how to fix it? sorry i am newbie guys, i am need more references , thank you
The lateinit field holds a reference to the listener that does something with the result of the time that you've picked.
So if you show the picker dialog without setting that listener first, when the onTimeSet(..) function is called, which happens when you tap "OK" on the picker dialog, the action field still hasn't been initialised, so an UninitializedPropertyAccessException will be thrown.
You just need to set the action listener using the setListener(..) function beforehand. It can even be empty as shown below.
val dialog = TimePickerFragment()
dialog.setListener {
// your listener logic
}
dialog.show(supportFragmentManager, "fragment-tag")
Update: In onTimeSet, you are passing a string to action(..). In your code, action is a listener that does something with the string that you pass to it. But you are passing a String to the listener before it has been set to anything, that is why you are seeing an error.
lateinit fields must be set before they can be used.
The quickest modification to your existing class would be to apply a listener by default:
// Create a new instance of TimePickerDialog and return it
return TimePickerDialog(activity, this, hour, minute, DateFormat.is24HourFormat(activity)).apply {
action = { dateTimeString ->
Log.d("LOG_TAG", "Time Selected -> $dateTimeString") // this outputs the String defined in onTimeSet to Logcat
}
}
Then if you want to assign a different listener, you do so with setListener().
Just before the function that uses the variable or instance, initialise it to a reasonable value or use this if its part of the class constructors

calendar test passes when using AndroidJunit4 only

I have a weird case. I am running a local test and using the Calendar class within that test.
when annotating the test class with #RunWith(AndroidJUnit4::class) the test passes, otherwise, the test fails.
the test code doesn't include any Android environment library
here is my class
class MyDateUtils(private val calendar: Calendar) {
fun getDates(): Long{
calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY)
return calendar.timeInMillis
}
}
and here is the test case, this one passes
#RunWith(AndroidJUnit4::class)
class MyDateUtilsTest {
private lateinit var calendar: Calendar
private lateinit var dateUtils: MyDateUtils
#Before
fun init() {
calendar = Calendar.getInstance()
calendar.timeInMillis = 1592422768000
dateUtils = MyDateUtils(calendar)
}
#Test
fun `when get dates is called with wednesday day should return sunday of the same week`() {
val expected = 1592163568000
val actual = dateUtils.getDates()
assertEquals(expected, actual)
}
}
now when I remove #RunWith(AndroidJUnit4::class) the test fails with this error message java.lang.AssertionError: Expected :1592163568000 Actual :1592768368000
P.S the expected behavior is that the getDates() method return Sunday within the same week. but without #RunWith(AndroidJUnit4::class) it return the following Sunday (The next week).
This might be something to do with the default locale that might be changed when you run with AndroidJUnit4.
With different locales the first day of the week is different and that could make the tests fail or pass depending on the runner.

How to set Text for EditText with Date Picker in Kotlin

I am new to Kotlin and android app development.
I am following a online course and one of the exercises is picking date to remind a to do item at certain time.
What i did is created EditText and implemented DatePicker. When user clicks date picker is opened and after I select the date, I can not set to text of EditText.
BTW I already checked other related questions and it says check your id to make sure you are referring to non null widget in correct layout file. I double checked it and it is not the problem
Here is the error.
Attempt to invoke virtual method 'void android.widget.EditText.setText(java.lang.CharSequence)' on a null object reference
//EditText on layout file
<EditText
android:id="#+id/calendarEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="Select your date"
android:focusable="false"
/>
//My Kotlin code
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add)
//DatePicker is called inside onCreate function
calendarEditText.setOnClickListener {
val newFragment = DatePickerFragment()
newFragment.show(supportFragmentManager, "datePicker")
}
}
// DateSet part of my DatePickerFragment
override fun onDateSet(view: DatePicker, year: Int, month: Int, dayOfMonth: Int) {
//selected date converted into one string
val date = "$year/$month/$dayOfMonth"
//this is where I got the error
calendarEditText.setText(date)
}
for me calendarEditText is null because you call it in a DatePickerFragment, so you are in a different context.
In your onDataSet, update edittext like that :
((MyActivity) activity).setEdt(date);
and in your activity, something like that :
public setEdt(date: String): void {
calendarEditText.setText(date)
}
or follow this tuto to avoid fragment

Android Kotlin open Link with button

i try to open an link with a button in Kotlin, but if i use this code
fun openNewTabWindow(urls: String, context: Context) {
val uris = Uri.parse(urls)
val intents = Intent(Intent.ACTION_VIEW, uris)
val b = Bundle()
b.putBoolean("new_window", true)
intents.putExtras(b)
context.startActivity(intents)
}
And in my button i use
openNewTabWindows("https://Google.com/")
It say it need context After url?
What does that mean?
openNewTabWindow(urls: String, context: Context) function needs 2 paramters, a String and a Context.
And in my button i use
openNewTabWindows("https://Google.com/")
You just called this function with 1 parameter, then of course
It say it need context After url.
You need to pass a Context as the second parameter. Since you say you are implementing the action of clicking a button (which is, inside #Override public void onClick(View v) {} in Java, or a Lambda with type (View) -> Unit in Kotlin), which is probably inside an Activity, and the reference of this may be changed, you can pass getContext() or for example MainActivity.this as the context needed for program, or
openNewTabWindows("https://Google.com/", context) // Kotlin version of getContext()
openNewTabWindows("https://Google.com/", this#MainActivity) // Kotlin version of MainActivity.this
May both OK.

Categories

Resources