Can't set text on text view in dialog fragment - android

I have a dialog fragment and I use databinding to bind views.
I can't set text on a text view after the dialog is created.
Here is my code :
class MyDialogFragment : DialogFragment() {
private lateinit var layout : FragmentMyDialogBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
layout = FragmentMyDialogBinding.inflate(inflater,container,false)
return layout.root
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
layout = FragmentMyDialogBinding.inflate(LayoutInflater.from(requireContext()))
layout.textView.text = "Initial text"
layout.button.setOnClickListener{
layout.textView.text = "Text changed"
Log.wtf("Text","${layout.textView.text}")
// Log shows the changed text but it is not visible on the ui.
}
val builder = MaterialAlertDialogBuilder(requireContext(), R.style.RoundShapeTheme)
builder.setView(layout.root)
return builder.create()
}
}
Log shows the changed text but it is not visible on the UI.
Does anybody have a solution for this ?

I just tested your code and it is resolved by removing the onCreateView().. You already use onCreateDialog() and it's enough for setting the binding object.
The layout is inflated twice, and probably the textView gets changed on the layout that is not on the UI. That is probably because the onCreateView() gets called after onCreateDialog(). So, when you change the text in the onCreateDialog() inflated layout the change is not appeared because the inflated layout by onCreateView() is laid on top of it.

In onCreateDialog() method set the custom views by the following code:
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
binding = DialogLayoutBinding
.inflate(LayoutInflater.from(getContext())); AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setView(binding.getRoot());
binding.dialogTextView.setText("I am Dialog's TextView");
return builder.create(); }

Related

Cannot change Views at Runtime in Dialogfragment

I want to set the drawable of a ImageView and a ProgressBar in a DialogFragment at runtime. Therefore i want to set the drawable in the onViewCreated and set the ProgressBar visible when the Positive Button of the Dialog is clicked.
But for reasons i do not know absolutely nothing happens. i also tried to call invalidate on the view to actively trigger a redraw but also nothing happens.
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.dialog_view, container, false)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val inflater = requireActivity().layoutInflater
val layout = inflater.inflate(R.layout.dialog_view, null)
val dialog = AlertDialog.Builder(requireContext())
.setTitle(R.string.dialog_title)
.setMessage(R.string.dialog_text)
.setView(layout)
.setPositiveButton(R.string.start, null)
.setNegativeButton(R.string.cancel, null)
.create()
return dialog
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
imageView.setImageDrawable(image) //image is a drawable
}
override fun onResume() {
super.onResume()
val d = dialog as AlertDialog?
d?.getButton(Dialog.BUTTON_POSITIVE)?.setOnClickListener {
progressBar.visibility = View.VISIBLE
}
}
Edit:
Found out that if i directly call something on the layout i pass to the dialog it is possible to change visibility of views.
val layout = inflater.inflate(R.layout.dialog_view, null)
layout.findViewById ....
This works but i still wonder why i cannot simply call with kotlin synthetics on the view.
The action that you want to be performed when the positive button is pressed should be added in the dialog build
.setPositiveButton(R.string.start){ _, _ ->
progressBar.visibility = View.VISIBLE
}

Android DialogFragment getDialog() and getView() returning null

I've got a DialogFragment that I need to show from a Fragment. This is my DialogFragment:
class MyDialogFragment : DialogFragment() {
companion object {
#JvmStatic
internal fun newInstance(): MyDialogFragment = MyDialogFragment()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.my_dialog_fragment container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<MaterialButton>(R.id.cancel_button)?.setOnClickListener { dismiss() }
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(requireActivity())
val dialogView = requireActivity().layoutInflater.inflate(R.layout.my_dialog_fragment, null)
builder.setView(dialogView)
dialogView.findViewById<Button>(R.id.cancel_button).setOnClickListener { dismiss() }
return builder.create()
}
fun setMyAction(action: () -> Unit) {
view?.findViewById<MaterialButton>(R.id.proceed_button)?.setOnClickListener { action() }
}
}
The way I'm calling it from the fragment as this:
btnSubmit.setOnClickListener(view -> {
MyDialogFragment myDialogFragment = new MyDialogFragment();
myDialogFragment.show(getChildFragmentManager(), MyDialogFragment.class.getSimpleName());
myDialogFragment.setMyAction(this::action);
});
The problem I have here is that it is impossible to call setMyAction from the calling fragment and call the action(), since in the dialog, view is null just like if I try to get it with dialog(). A second problem here is: why is the onCreateView never called, just like onViewCreated?
Thanks in advance!
The problem I have here is that it is impossible to call setMyAction from the calling fragment and call the action(), since in the dialog, view is null just like if I try to get it with dialog().
The problem that you are experiencing now is that show() is asynchronous. The dialog will be created some time later, not by the time show() has returned.
The problem that you are not yet experiencing, but will, is that on a configuration change, your activity and its fragment will be recreated by default, and your OnClickListener will be lost.
Rather than pushing an event handler in, have the DialogFragment expose results (e.g., via a shared ViewModel).
A second problem here is: why is the onCreateView never called, just like onViewCreated?
You overrode onCreateDialog(). You cannot both use onCreateDialog() and onCreateView()/onViewCreated(). Pick one and use it.

How to pass the events/values from alertdialog or popup window to an activity in Android MVP?

I'm following the MVP pattern for my app and I am having confusion about adding the event listeners to the alert dialog and also passing the text view value from alert dialog to activity back. Which is the best approach?.
Use of Listener is the best approach. How? Let see-
I am assuming you are using custom dialog, for this apply these steps to get proper callbacks-
Make an Interface inside your CustomDialog class which will return you the callback event.
Implement that interface in your Presenter/ViewModel class.
Now your Presenter/ViewModel Override that callback method and from here you can use that for your next task.
Pass Presenter/ViewModel reference to your Custom Dialog as the instance of Interface.
Now call the interface method where you want inside your Dialog.
A Quick Code Example(in kotlin)
CustomDialog
class CustomDialog : DialogFragment() {
var listener: Listener? = null
var messageText = ""
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(this.activity)
val rootView = activity?.layoutInflater?.inflate(R.layout.dialog_custom, null)
isCancelable = false
val messageTextView: TextView? = rootView?.findViewById(R.id.messageTextView)
val okButton: Button? = rootView?.findViewById(R.id.okButton)
if (messageText.isNotBlank()) {
messageTextView?.text = messageText
}
okButton?.setOnClickListener {
listener?.customOkClicked()
dismiss()
}
builder.setView(rootView)
return builder.create()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
dialog?.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
return super.onCreateView(inflater, container, savedInstanceState)
}
interface Listener {
fun customOkClicked()
}
}
YouPresenter
class MyPresenter: CustomDialog.Listener {
.
.
// other code
override fun customOkClicked() {
// your next step
}
}
YourActivity
class YourActivity: Activity() {
.
.
fun showCustomDialog() {
CustomDialog().apply {
this.listener = presenter // reference of your presenter class.
this.messageText = msg
}.show(supportFragmentManager, "custom_dialog_tag")
}
}
Note: You can apply same approach with the MVVM, do the same thing with your ViewModel in case.
Hope this is the answer you are looking for.
You should LayoutInflater to inflate your dialog into a view. Then you can easily retrive the value as follows :
LayoutInflater layoutInflater = LayoutInflater.from(context);
View view = layoutInflater.inflate(R.layout.dialog, null, false);
EditText textView = view.findViewById(R.id.editText);
String text = textView.getText();

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.

DialogFragment rounded corners - how to set transparency

I've made custom layout for dialogFragment with rounded corners but when dialog is called corners are rounded he looks like below.
https://i.imgur.com/aE4PMhZ.png
I know i need to set transparency dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
but I dont know where to put this in my code, additionally I'm using Kotlin.
Below is the part of code where Dialog is called.
myDialog = Event_Dialog.newInstance(args,args1)
myDialog.show(fragmentManager, "MyDialog")
This is Event_Dialog class.
class Event_Dialog : DialogFragment() {
companion object {
fun newInstance(bundle: String, bundle1: String): Event_Dialog {
//description
val args: Bundle = Bundle()
args.putString("desc", bundle)
//link
args.putString("link", bundle1)
val fragmentDialog = Event_Dialog()
fragmentDialog.arguments = args
return fragmentDialog
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val x = inflater.inflate(R.layout.event_detail, container, false)
some code.......
return x
Could you tell me guys where I should set the transparency of custom background ?
Thanks !
just put it in oncreatedialog like :
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
return dialog
}
and i suggest that you use a framelayout as a root view for your dialog ( match parent frame ) and design your center layout in it

Categories

Resources