kotlinx.android.synthetic returns null in DialogFragment - android

Before I begin, I have read similarly worded posts like this one, but they are not working for me.
As I explain in the title, I get an NPE when I click the positive button ("ok" in this case). If anybody can point out what I am doing incorrectly that'll be great! Below is a abstracted version of my setup
MainFragment.kt
class MainFragment: DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?) =
AlertDialog.Builder(context!!)
.setView(FragmentMainBinding.inflate(LayoutInflater.from(context)).root)
.setTitle(R.string.title_main)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ -> onPositiveButtonTapped() }
.create()
private fun onPositiveButtonTapped() {
val g = arrayListOf(ground_nbr_edit_text.text.toString()) // NullPointerException
// ...
}
}
fragment_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="#+id/ground_nbr_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>

DialogFragment calls onCreateView() after onCreateDialog() and then uses that view for the dialog's content, replacing whatever you had already set in onCreateDialog(). So move your view creation into onCreateView():
class MainFragment: DialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?) =
FragmentMainBinding.inflate(inflater).root
override fun onCreateDialog(savedInstanceState: Bundle?) =
AlertDialog.Builder(context!!)
.setTitle(R.string.title_main)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { _, _ -> onPositiveButtonTapped() }
.create()
private fun onPositiveButtonTapped() {
val g = arrayListOf(ground_nbr_edit_text.text.toString()) // NullPointerException
// ...
}
}

Related

Fragment not attached to a context error when calling function from XML with binding expression

I am trying to call a function in my fragment via expression binding from my XML file in "android:onclick...", but it will not work. The error is that the fragment is not attached to a context.
It is the
MaterialAlertDialogBuilder(requireContext())
which gives me headache.
How do I give the context to the fragment?
I have seen similar questions regarding that topic, but none that helped me.
Any help is much appreciated.
ItemDetailFragment.kt:
class ItemDetailFragment : Fragment() {
private lateinit var item: Item
private val viewModel: InventoryViewModel by activityViewModels {
InventoryViewModelFactory(
(activity?.application as InventoryApplication).database.itemDao()
)
}
private val navigationArgs: ItemDetailFragmentArgs by navArgs()
private var _binding: FragmentItemDetailBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentItemDetailBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val id = navigationArgs.itemId
binding.viewModel = viewModel
binding.fragment = ItemDetailFragment()
}
/**
* Displays an alert dialog to get the user's confirmation before deleting the item.
*/
fun showConfirmationDialog() {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(android.R.string.dialog_alert_title))
.setMessage(getString(R.string.delete_question))
.setCancelable(false)
.setNegativeButton(getString(R.string.no)) { _, _ -> }
.setPositiveButton(getString(R.string.yes)) { _, _ ->
deleteItem()
}
.show()
}
/**
* Called when fragment is destroyed.
*/
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
fragment_item_detail.kt:
<?xml version="1.0" encoding="utf-8"?><!--
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.example.inventory.InventoryViewModel" />
<variable
name="fragment"
type="com.example.inventory.ItemDetailFragment" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="#dimen/margin"
tools:context=".ItemDetailFragment">
<Button
android:id="#+id/delete_item"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/margin"
android:onClick="#{()->fragment.showConfirmationDialog()}"
android:text="#string/delete"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/sell_item" />
</androidx.constraintlayout.widget.ConstraintLayout>
That is the error i am getting:
java.lang.IllegalStateException: Fragment ItemDetailFragment{e562873} (c6ab2144-3bdc-410b-91eb-e5668e8b617a) not attached to a context.
You should not pass your fragment instance as a data binding variable.
You could define a Boolean mutable live data variable in your InventoryViewModel and show the dialog when it changes:
private val _showConfirmation = MutableLiveData(false)
val showConfirmation
get() = _showConfirmation
fun onShowConfirmation() {
_showConfirmation.value = true
}
fun onConfirmationShown() {
_showConfirmation.value = false
}
Then, define an observer for this property in your ItemDetailFragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val id = navigationArgs.itemId
binding.viewModel = viewModel
binding.executePendingBindings()
viewModel.showConfirmation.observe(viewLifecycleOwner) {
if (it) {
showConfirmationDialog()
viewModel.onConfirmationShown()
}
}
}
Finally, remove the fragment variable from the XML and change your Button's onClick as:
<Button
...
android:onClick="#{() -> viewModel.onShowConfirmation()}"
/>

Android: Button onClicklistener not working after switching fragment

I have a very weird problem. When I navigate from Fragment 1 to Fragment 2 using a btn.setOnClickListener and then navigating back from Fragment 2 to Fragment 1 using the back button, the btn.setOnClickListener in Fragment 1 does not work anymore and therefore is not able to navigate to Fragment 2 again.
Here is my code:
Button XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
// Custom Background for the button
<com.google.android.material.appbar.MaterialToolbar
android:clickable="false"
android:id="#+id/materialToolbar"
android:layout_width="match_parent"
android:layout_height="90dp"
android:background="#color/btnColorGray"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
</com.google.android.material.appbar.MaterialToolbar>
<com.google.android.material.button.MaterialButton
android:clickable="true"
android:focusable="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
Main XML
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.view.fragments.home.calibrateAndRepair.CalibrateRepairMessageFragment">
... some other stuff
<!-- Included the button -->
<include
android:id="#+id/calibrate_repair_btn"
layout="#layout/calibrate_repair_btn"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
BaseFragment for databinding
abstract class BaseFragment<out T: ViewDataBinding>(val layout: Int) : Fragment() {
abstract val viewModel: ViewModel
private val _navController by lazy { findNavController() }
val navController: NavController
get() = _navController
fun navigateTo(fragment: Int, bundle: Bundle? = null) {
_navController.navigate(fragment, bundle)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return DataBindingUtil.inflate<T>(inflater, layout, container, false).apply {
lifecycleOwner = viewLifecycleOwner
setVariable(BR.viewModel, viewModel)
Timber.d("Created BaseFragment and binded View")
}.root
}
}
EmailFragment for initializing the button
abstract class EmailFragment<out T: ViewDataBinding>(
layout: Int,
private val progressBarDescription: ArrayList<String>,
private val stateNumber: StateProgressBar.StateNumber
) : BaseFragment<T>(layout) {
abstract val next: Int
abstract val bundleNext: Bundle?
// getting the button from the button.xml
private val btnNext: MaterialButton by lazy { btn_next_calibrate }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// ... some other initializing which constantly work!
initButton()
}
// Initializing the button
private fun initButton() {
btnNext.setOnClickListener {
navigateTo(next, bundleNext)
Timber.d("Button clicked")
}
}
}
Fragment 1
#AndroidEntryPoint
class CalibrateRepairMessageFragment(
private val progressBarDescription: ArrayList<String>,
#StateNumberOne private val stateNumber: StateProgressBar.StateNumber,
) : EmailFragment<FragmentCalibrateRepairMessageBinding>(
R.layout.fragment_calibrate_repair_message,
progressBarDescription,
stateNumber
) {
// Overriding the values from EmailFragment
override val next: Int by lazy { R.id.action_calibrateRepairMessageFragment_to_calibrateRepairUserDataFragment }
override val bundleNext: Bundle by lazy { bundleOf("calibrate_repair_toolbar_text" to toolbarText) }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// ... some other stuff
}
}
Fragment 2
#AndroidEntryPoint
class CalibrateRepairUserDataFragment(
private val progressBarDescription: ArrayList<String>,
#StateNumberTwo private val stateNumber: StateProgressBar.StateNumber,
) : EmailFragment<FragmentCalibrateRepairUserDataBinding>(
R.layout.fragment_calibrate_repair_user_data,
progressBarDescription,
stateNumber
) {
override val next: Int by lazy { R.id.action_calibrateRepairUserDataFragment_to_calibrateRepairDataOverviewFragment }
override val bundleNext: Bundle by lazy { bundleOf("calibrate_repair_toolbar_text" to toolbarText) }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}
I tried to delete everything that is not important for the question. You can ignore the constructor of BaseFragment, EmailFragment, CalibrateRepairMessageFragment and CalibrateRepairUserDataFragment. I am using navigation component and dagger-hilt.
I appreciate every help, thank you.
P.S: I've noticed that using button:onClick in the .xml file solves this problem but in this situation I can't use the xml version.
The problem with this should be your lazy initialization of btnNext.
The Fragment1 state is saved when navigating to Fragment2. When navigating back the XML View will be reloaded but the lazy value of btnNext won't change as it is already initialized and is pointing to the old reference of the button view. Thus your OnClickListener will always be set to the old reference.
Instead of assigning your button lazily you should assign it in EmailFragment's onCreateView()
PS: Also from btn_next_calibrate I suppose you are using kotlin synthetic view binding. If so you would not have to use a class variable.

Loading URL in WebView from Button Click in BottomSheetDialogFragment via Data-Binding

0. Problem
This question by gave me the idea to implement the Data Binding Library for the purpose of opening a Link in a Webview-Fragment on click of a Button in a Bottom Sheet Fragment.
I was able to implement the Data Binding as seen in the other Question (Link), but the WebView doesn't load the new URL when a Button is clicked in said Bottom Sheet Fragment. I get the feedback from the console that the Button was clicked and the LiveData was changed though.
So, I thought the WebView does reload automatically when the LiveData changes but that doesn't seem to be the case...so I do not see my Error and not sure if I implemented everything correctly.
Hopefully someone can help me.
1. Respective Classes
1.1. WebViewFragment
class WebviewFragment : Fragment() {
private lateinit var webView: WebView
companion object {
fun newInstance() = WebviewFragment()
}
private lateinit var viewModel: WebViewViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding: FragmentMainWebviewBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_main_webview, container, false)
viewModel = ViewModelProvider(this).get(WebViewViewModel::class.java)
binding.webViewModel = viewModel
binding.lifecycleOwner = this
return binding.root
}
#SuppressLint("SetJavaScriptEnabled", "JavascriptInterface")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
webView = webViewMain
webView.settings.javaScriptEnabled = true
}
}
1.2. WebViewModel
class WebViewViewModel : ViewModel() {
val webViewUrl = MutableLiveData<String>().apply{ value = "file:///android_asset/html_files/gallery_page.html" }
companion object WebViewUrlLoader {
#BindingAdapter("loadUrl")
#JvmStatic
fun WebView.setUrl(url: String) {
this.loadUrl(url)
}
}
}
1.3. WebView Layout (XML)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="webViewModel"
type="com.example.ui.main.webview.WebViewViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/main_screen_webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainFragmentGalleryView"
>
<WebView
android:id="#+id/webViewMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:loadUrl="#{webViewModel.webViewUrl}"
android:paddingBottom="52dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
1.4. BottomSheet Fragment
class BottomSheetFragment : BottomSheetDialogFragment() {
private var fragmentView: View? = null
private lateinit var viewModel: WebViewViewModel
companion object {
fun newInstance() = BottomSheetFragment()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
fragmentView = inflater.inflate(R.layout.view_modal_bottom_sheet, container, false)
return fragmentView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(WebViewViewModel::class.java)
initView()
}
override fun getTheme(): Int {
return R.style.Theme_NoWiredStrapInNavigationBar
}
private val mBottomSheetBehaviorCallback: BottomSheetCallback = object : BottomSheetCallback() {
var isBottomSheetUp = false
override fun onSlide(bottomSheet: View, slideOffset: Float) {
//TODO("Not yet implemented")
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
isBottomSheetUp = false
dismiss()
} else isBottomSheetUp = true
}
}
override fun setupDialog(dialog: Dialog, style: Int) {
//super.setupDialog(dialog, style)
val contentView =
View.inflate(context,
R.layout.view_modal_bottom_sheet, null)
dialog.setContentView(contentView)
val layoutParams =
(contentView.parent as View).layoutParams as CoordinatorLayout.LayoutParams
val behavior = layoutParams.behavior
if (behavior != null && behavior is BottomSheetBehavior<*>) {
behavior.setBottomSheetCallback(mBottomSheetBehaviorCallback)
}
}
private fun initView() {
action_my_pictures.setOnClickListener {
viewModel.webViewUrl.value = "https://www.google.com/"
Log.d("BottomSheet", "Button 1 Clicked ${viewModel.webViewUrl.value}")
}
action_favorites.setOnClickListener {
viewModel.webViewUrl.value = "https://www.hotmail.de/"
Log.d("BottomSheet", "Button 2 Clicked ${viewModel.webViewUrl.value}")
}
action_ranking.setOnClickListener {
viewModel.webViewUrl.value = "https://amazon.com/"
Log.d("BottomSheet", "Button 3 Clicked ${viewModel.webViewUrl.value}")
}
action_hall_of_fame.setOnClickListener {
viewModel.webViewUrl.value = "https://m.daum.net/"
Log.d("BottomSheet", "Button 4 Clicked ${viewModel.webViewUrl.value}")
}
action_liked_pictures.setOnClickListener {
viewModel.webViewUrl.value = "https://m.nate.com/"
Log.d("BottomSheet", "Button 5 Clicked ${viewModel.webViewUrl.value}")
}
action_events.setOnClickListener {
viewModel.webViewUrl.value = "https://www.danawa.com/"
Log.d("BottomSheet", "Button 6 Clicked ${viewModel.webViewUrl.value}")
}
action_close_bottom_sheet.setOnClickListener {
dismiss()
}
}
}
1.5. BottomSheet Layout (XML)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="webViewModel"
type="com.example.ui.main.webview.WebViewViewModel" />
</data>
<LinearLayout
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/bottomNavigationViewBackground"
android:orientation="vertical"
app:behavior_hideable="true"
app:behavior_peekHeight="auto"
app:layout_behavior="#string/bottom_sheet_behavior"
app:behavior_fitToContents="true">
[7 Image/Icons as Buttons]
Example:
<LinearLayout
android:id="#+id/action_my_pictures"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:orientation="vertical"
android:clickable="true"
android:focusable="true">
<ImageView
android:id="#+id/icon_my_pictures"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="center_horizontal"
android:src="#drawable/ic_user_color" />
<TextView
android:id="#+id/icon_my_pictures_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:singleLine="true"
android:text="My Gallery"
android:paddingTop="6dp"
android:textAlignment="center"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</layout>
2. LOG output (for Button Clicks)
D/BottomSheet: Button 1 Clicked https://www.google.com/
D/BottomSheet: Button 2 Clicked https://www.hotmail.de/
D/BottomSheet: Button 3 Clicked https://amazon.com/
D/BottomSheet: Button 4 Clicked https://m.daum.net/
D/BottomSheet: Button 4 Clicked https://m.daum.net/
D/BottomSheet: Button 5 Clicked https://m.nate.com/
D/BottomSheet: Button 6 Clicked https://www.danawa.com/
A lot of thanks in advance.
I think problem is in scope of life that you provide for viewModel.
Currently you code looks like this:
viewModel = ViewModelProvider(this).get(WebViewViewModel::class.java)
This code is invoked inside two different fragments, and because ViewModelProvider is initialized with current instance of fragment (this). Your view model is only available in this scope (fragment scope). So at the end for each fragment you will get new ViewModel. To share ViewModel between fragments you should use different approach:
Instantiate ViewModelProvider with activity. (ViewModel will be share across all fragments inside activity)
Instantiate ViewModelProvider with parentFragment. (ViewModel will be shared across all child fragments)
sample:
viewModel = ViewModelProvider(requireActivity()).get(WebViewViewModel::class.java)
viewModel = ViewModelProvider(parentFragment).get(WebViewViewModel::class.java)
please also check:
How to scope ViewModels properly?
https://developer.android.com/reference/android/arch/lifecycle/ViewModelProviders

DialogFragment with view model not working with data binding

I've created one dialog fragemnt with view model (mvvm). Dialog consist of one button (custom view). when using view model with data binding, button click is not working when livedata change.I'm using boolean value to check if button is clicked or not. What is causing issue? Also suggest any other approach if needed.
profile_dialog_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewmodel"
type="com.test.ui.ProfileDialogViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.ProfileDialog">
<com.google.android.material.button.MaterialButton
android:id="#+id/login"
style="#style/TextAppearance.MaterialComponents.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Login"
android:onClick="#{() -> viewmodel.onLoginButtonClick()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
ProfileDialog.kt
class ProfileDialog : DialogFragment() {
companion object {
fun newInstance() = ProfileDialog()
}
private val viewModel: ProfileDialogViewModel by viewModel()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = ProfileDialogFragmentBinding.inflate(inflater, container, false)
.apply {
this.lifecycleOwner = this#ProfileDialog
this.viewmodel = viewmodel
}
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel.startLogin.observe(viewLifecycleOwner, Observer {
Log.d("insta", "This is working")
if (it == null) return#Observer
if(it) {
Log.d("insta", "This is not working")
val loginIntent = Intent(this.context, LoginActivity::class.java)
this.context?.startActivity(loginIntent)
}
})
}
}
ProfileDialogViewModel.kt
class ProfileDialogViewModel : ViewModel() {
private val _startLogin = MutableLiveData<Boolean>(false)
val startLogin: LiveData<Boolean>
get() = _startLogin
fun onLoginButtonClick() {
Log.d("insta", "This ain't working")
_startLogin.postValue(true)
}
}
Your viewmodel is defined in
private val viewModel: ProfileDialogViewModel by viewModel()
So, pay attention to viewModel. The problem located in
this.viewmodel = viewmodel
where this points to ProfileDialogFragmentBinding. Here you assinging ProfileDialogFragmentBinding.viewmodel = ProfileDialogFragmentBinding.viewmodel - that's why it's not working.
To solve problem, properly assign it like that:
this.viewmodel = viewModel

Android getting OutOfMemoryError when instantiating a DialogFragment

I am reading this from Google about using DialogFragment. Everything works until the section Creating a Custom Layout: when the call is done to display a DialogFragment with a custom layout I get a OutOfMemoryError exception.
I have just slighty modified the code from the article, and my app only contains the 3 elements below:
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
fab.setOnClickListener { view ->
showDialog()
}
}
fun showDialog() {
// Create an instance of the dialog fragment and show it
val dialog = FireMissilesDialogFragment()
dialog.show(supportFragmentManager, "NoticeDialogFragment")
}
}
FireMissilesDialogFragment.kt
class FireMissilesDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
val builder = AlertDialog.Builder(it)
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
builder.setView(layoutInflater.inflate(R.layout.dialog_layout, null))
// Add action buttons
.setPositiveButton(R.string.ok) { dialog, id ->
Log.d("FireMissiles", "TEST")
}
.setNegativeButton(R.string.cancel) { dialog, id ->
getDialog().cancel()
}
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
}
dialog_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<EditText
android:id="#+id/username"
android:inputType="textEmailAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="4dp"
android:hint="#string/ok" android:importantForAutofill="no"/>
<EditText
android:id="#+id/password"
android:inputType="textPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="16dp"
android:fontFamily="sans-serif"
android:hint="#string/cancel" android:importantForAutofill="no"/>
</LinearLayout>
My code has only these 3 elements above. I guess there is somewhere a memory leak but I cannot find it.
Does someone have an idea?
Answers to comments and solution:
#Sam I do not use any image, this project is just a attempt to use DialogFragment,
hence it is based on standard empty Activity project with fab, only thing I did not show is the activity_main.xml but it is the standard one.
#Tobias, thanks for the hint but it did not solve the problem :(
#user8159708, thank you!! that solved the problem. My code is now (and it is the only thing I changed):
class FireMissilesDialogFragment : DialogFragment() {
lateinit var mView: View
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
mView = inflater.inflate(R.layout.dialog_layout, container, false)
setDialog()
return mView
}
fun setDialog(){
activity?.let {
val builder = AlertDialog.Builder(it)
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
builder.setView(mView)
// Add action buttons
.setPositiveButton(R.string.ok) { dialog, id ->
Log.d("FireMissiles", "TEST")
}
.setNegativeButton(R.string.cancel) { dialog, id ->
Log.d("FireMissiles", "TEST")
//getDialog().cancel()
}
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
}
Don't create your dialog using an alert builder.
Remove your override of onCreateDialog.
Inflate your view in onCreateView:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.dialog_layout, container, false)
}
Add some buttons to your layout yourself, and set your onClickListeners manually after inflating your view.
This sounds like loop.
Try to remove getDialog().cancel() and just return null instead.
You don't need to explicitely close Dialoges.
I think you are doing it wrong. You don't need to use AlertDialog.Builder as you are already extending from DialogFragment.
You can follow this link.
It is in Java, but in Kotlin it will be similar.

Categories

Resources