I have a custom dialog in kotlin fragment which have 2 button and a progress bar in fuction submitSpk()
class RincianPembelianFragment : androidx.fragment.app.Fragment(), BlockingStep {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater.inflate(R.layout.fragment_spk_rincian_bayar, container, false)
//initialize your UI
return v
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity?.supportFragmentManager?.popBackStack(null, androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE)
setContext(activity!!.applicationContext)
}
fun submitSpks (){
val inflater = context?.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val builder = AlertDialog.Builder(context!!)
val dialogView = inflater.inflate(R.layout.activity_signature, null)
builder.setView(dialogView)
builder.setTitle("Tanda Tangan Pembeli")
//builder.show()
builder.setCancelable(false)
dialogView.clear.setOnClickListener {
dialogView.signature_view.clearCanvas()
}
dialogView.save.setOnClickListener {
submitTtd() // or some fuction else
dialogView.save.visibility = View.INVISIBLE
dialogView.progressBar.visibility = View.VISIBLE
}
builder.setNegativeButton(""){ dialog: DialogInterface?, which: Int ->
}
builder.show()
}
fun submitTtd(){
// here there will be a crud transaction and visible/invisible button save
}
}
my custom dialog is look like this
i want to visibility gone the button save dialogView.save.visibility = View.VISIBLE from another fuction e.g saveTtd()
how can i do this? i have tried this but my dialog can't show.. anyone can help me? Thanks before
Storing the dialogView in a global variable should do the job.
Also, call the build() method on the AlertDialog.Builder instance before showing the dialog:
class RincianPembelianFragment : androidx.fragment.app.Fragment(), BlockingStep {
private lateinit var dialogView: View
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater.inflate(R.layout.fragment_spk_rincian_bayar, container, false)
//initialize your dialog view
dialogView = inflater.inflate(R.layout.activity_signature, null)
return v
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity?.supportFragmentManager?.popBackStack(null, androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE)
setContext(activity!!.applicationContext)
}
fun submitSpks() {
// Using requireContext() is suggested
val builder = AlertDialog.Builder(requireContext())
builder.setView(dialogView)
builder.setTitle("Tanda Tangan Pembeli")
builder.setCancelable(false)
dialogView.clear.setOnClickListener {
dialogView.signature_view.clearCanvas()
}
dialogView.save.setOnClickListener {
submitTtd() // or some fuction else
}
builder.setNegativeButton(""){ dialog: DialogInterface?, which: Int ->
}
// Build the dialog builder before showing it
val dialog = builder.build()
dialog.show()
}
fun submitTtd(){
// here there will be a crud transaction and visible/invisible button save
dialogView.save.visibility = View.INVISIBLE
dialogView.progressBar.visibility = View.VISIBLE
}
}
Related
I am not sure why I cannot update the imageView of a custom dialog layout xml which is opened from inside a fragment.
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setView(R.layout.qr_code_layout_dialog);
imageFromLayout.setImageBitmap(myBitmap);
builder.show();
I have also tried to use a Dialog object (not AlertDialog) but it will not even open, therefore AlertDialog opens but will not show any images. Nor will it allow me to update the images from within the java code.
How can I update the imageView image of the layout dialog from within the fragment?
You can try make realisation of it
abstract class ObservableDialog<T>(private val callBack: (T) -> Unit): DialogFragment(){
companion object{
const val TAG = "MY_TAG"
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setLayoutParam()
}
fun doMyChoice(obj: T){
callBack(obj)
dismiss()
}
private fun setLayoutParam(){
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
}
}
Call it like
val dialog = ObservableDialog<String>{
//do something with result
}
dialog.show(supportFragmentManager, ObservableDialog.TAG)
I loved using Kotlin synthetic for its simplicity and code elegance but now they made it depricated and push you to use those ugly view bindings.
There are plenty of answers on how to use it in activites and Fragments, but could not find any examples for custom layout alert dialogs.
Here is the code which worked perfectly with Kontlin synthetic.
import kotlinx.android.synthetic.main.dialog_reward.*
class RewardDialog: DialogFragment() {
private var mView: View? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return mView
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
mView = it.layoutInflater.inflate(R.layout.dialog_reward, null)
AlertDialog.Builder(it).apply {
setView(mView)
}.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//reference layout elements by name freely
}
override fun onDestroyView() {
super.onDestroyView()
mView = null
}
}
How do I migrate this to view bindings?
You can simply use generated ViewBinding views here and not use onCreateDialog
#AndroidEntryPoint
class RewardDialog : DialogFragment() {
private var binding: DialogRewardBinding by autoCleared()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, R.style.Theme_MaterialComponents_Light_Dialog_MinWidth)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = DialogRewardBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//reference layout elements by name freely
binding.tvReward.setOnClickListener { }
}
}
autoCleared() is an extension function which will null out all the view in onDestroy() taken from google's architecture component sample here
You can set R.style.Theme_MaterialComponents_Light_Dialog_MinWidth theme in onCreate() so that DialogFragment follows the minWidth defined in Material Compnent theme on deferent screen sizes just like AlertDialog
Edit:
If you are not using the material component library then you can set the width in onViewCreated() using the Kotlin extension.
setWidthPercent(ResourcesCompat.getFloat(resources, R.dimen.dialogWidthPercent).toInt())
Kotlin extenstion function
fun DialogFragment.setWidthPercent(percentage: Int) {
val percent = percentage.toFloat() / 100
val displayMetrics = Resources.getSystem().displayMetrics
val rect = displayMetrics.run { Rect(0, 0, widthPixels, heightPixels) }
val percentWidth = rect.width() * percent
dialog?.window?.setLayout(percentWidth.toInt(), ViewGroup.LayoutParams.WRAP_CONTENT)
}
I have ended up with the following solution. Thanks to #Kishan Maurya for the hint about binding.root.
private var _binding: DialogRewardBinding? = null
private val binding get() = _binding!!
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.run {
//initiate the binding here and pass the root to the dialog view
_binding = DialogRewardBinding.inflate(layoutInflater).apply {
//reference layout elements by name freely here
}
AlertDialog.Builder(this).apply {
setView(binding.root)
}.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
class RewardDialog : DialogFragment() {
private var mView: View? = null
private lateinit var dialogBinding: DialogRewardBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// either this way we can init dialogBinding
dialogBinding = DialogRewardBinding.inflate(inflater, container, false)
return dialogBinding.root
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
// either this way we can init dialogBinding
dialogBinding = DataBindingUtil.setContentView(it, R.layout.dialog_reward)
AlertDialog.Builder(it).apply { setView(mView) }.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
with(view) {
myText.text = "Demo"
}
}
override fun onDestroyView() {
super.onDestroyView()
mView = null
}
}
instead of mView, you can use dialogBinding.root
layout
<?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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/myText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="demo"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
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
}
I've tried to invoke DialogFragment from custom view:
DetailsDialogFragment
.newInstance(newSelectedDate, adapterItems[newPosition].progress)
.apply {
show(childFragmentManager, SCORE_DETAILS_DIALOG_TAG)
}
where DetailsDialogFragment looks like this:
class DetailsDialogFragment : AppCompatDialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return requireActivity().let {
val dialog = Dialog(requireContext(), R.style.CustomDialog)
dialog.window?.setDimAmount(BaseDialogFragment.SCRIM_OPACITY)
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_details_dialog, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.rootView.apply {
findViewById<TextView>(R.id.titleTextView).text = arguments?.getString(ARGS_MONTH)
findViewById<ActionButton>(R.id.button).setOnClickListener {
dismiss()
}
findViewById<ImageView>(R.id.closeImageView).setOnClickListener {
dismiss()
}
}
}
companion object {
fun newInstance(
month: String,
score: Int
): DetailsDialogFragment {
return DetailsDialogFragment()
.apply {
arguments = bundleOf(
ARGS_MONTH to month,
ARGS_SCORE to score
)
}
}
}
}
But I receive the following error:
IllegalStateException: Fragment DetailsDialogFragment has not been attached yet.
at androidx.fragment.app.Fragment.getChildFragmentManager(Fragment.java:980)
...
Is it possible to invoke DialogFragment from custom view at all?
The reason of this exception is that you're trying to use the childFragmentManager of your freshly newly created instance, which is of course not possible since the Dialog fragment hasn't yet has its internals initialized yet (including its childFragmentManager).
If you're using AndroidX I'd use the findFragment extension method inside your custom view and try to do:
Inside your custom view
val dialogFragment = DetailsDialogFragment
.newInstance(newSelectedDate, adapterItems[newPosition].progress)
dialogFragment.show(findFragment().childFragmentManager, SCORE_DETAILS_DIALOG_TAG)
I created an interface and extended to the class
class MoviesandTv(override var mprogressdialog: Dialog) : Fragment() , CDialogs {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View = inflater.inflate(R.layout.moviesandtv, container, false)
setHasOptionsMenu(true)
mlinearLayoutManagerrr = LinearLayoutManager(activity)
mlinearLayoutManagerrr!!.reverseLayout = true
mlinearLayoutManagerrr!!.stackFromEnd = true
rc_listing_movies_tv = view.rc_list_movies_tv
showCustomProgressDialog()
showdata_movies()
return view
}
And hide my dialog bar on onbinderview when the data is loaded
fun showdata_movies(){
optionsss = FirebaseRecyclerOptions.Builder<Data>().setQuery(db_movies, Data::class.java).build()
mfirebaseRecyclerAdapterrr = object : FirebaseRecyclerAdapter<Data, ViewHolder_Movies_TV>(optionsss!!) {
override fun onBindViewHolder(viewHoldermovies: ViewHolder_Movies_TV, i: Int, model: Data) {
hideCustomProgressDialog()
viewHoldermovies.setDetails(activity, model.movie_title, model.review, model.date)
But is only shows the dialog box and does no hide it from onbnderview
my implemented method are
override fun showCustomProgressDialog() {
mprogressdialog = context.let { Dialog(it!!) }
mprogressdialog.setContentView(R.layout.custom_dialog)
mprogressdialog.show() }
override fun hideCustomProgressDialog() {
mprogressdialog!!.dismiss()
}
Because you are using FirebaseRecyclerAdapter, the option that you have is to override the following method and hide the ProgressBar, like in the following lines of code:
#Override
public void onDataChanged() {
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
}