DialogFragment custom view no background - android

I'm trying to inflate a custom view in a DialogFragment but the container view's are not showing up. Note I haven't found any post that matches this exactly, so if there is, please share, TIA.
here is my XML (very simple)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="#drawable/billnotifselectiondialog"
android:layout_marginHorizontal="45dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<Button
android:id="#+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:background="#color/secondary_200"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
My Class:
class BillNotifSelectionDialog: DialogFragment() {
private lateinit var binding: TestDialogLayoutBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = TestDialogLayoutBinding.inflate(LayoutInflater.from(context),container, false).apply {
binding = this
}.root
}
I'm invoking the class in my fragment with
BillNotifSelectionDialog().show(parentFragmentManager, "tag")
Here is the result, I can only see the button, not the ConstraintLayout:

This is a common issue of using ConstraintLayout as the root layout of DialogFragments.
One fix is to Replace it with RelativeLayout; but it's not recommended as per documentation quote:
For better performance and tooling support, you should instead build your layout with ConstraintLayout.
Instead of that you'd fix it programmatically by designating the dialog layout to match the parent with dialog.window.setLayout:
class BillNotifSelectionDialog : DialogFragment() {
//..... rest of code omitted
override fun onStart() {
super.onStart()
dialog!!.window!!.setLayout(MATCH_PARENT, MATCH_PARENT)
}
}

you need to override onCreateDialog instead of onCreateView in DialogFragment.
class BillNotifSelectionDialog : DialogFragment() {
private lateinit var binding: TestDialogLayoutBinding
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding = TestDialogLayoutBinding.inflate(layoutInflater)
return AlertDialog.Builder(requireContext())
.setView(binding.root)
.create()
}
to test the result, I changed your xml code that the background color is teal_200 and button color is purple_700
the code result

Related

Can't find views from not activity_main.xml

I'm making app with custom dialog fragment. I created a separate xml file test_parameters.xml for this dialog fragment, but when I try to refer to views there by their id, Android Studio doesn't see them. Only views from activity_main are available. What's the reason and how can I fix it?
MainActivity.kt:
package com.example.meltflowratecalculator
import...
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
lateinit var methodsSpinner: Spinner
lateinit var timeSpinner: Spinner
lateinit var result: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.settingsButton.setOnClickListener {
var dialog = CustomDialogFragment()
dialog.show(supportFragmentManager, "customDialog")
}
}
}
CustomDialogFragment.kt:
package com.example.meltflowratecalculator
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
class CustomDialogFragment : DialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
var rootView: View = inflater.inflate(R.layout.test_parameters, container, false)
return rootView
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/settings_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
test_parameters.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/testing_method_title"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Spinner
android:id="#+id/methods"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:entries="#array/methods"
android:minHeight="48dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/result" />
</androidx.constraintlayout.widget.ConstraintLayout>
You're getting that binding object by calling ActivityMainBinding#inflate - ActivityMainBinding is automatically generated from activity_main.xml (hence the name). It only contains bindings for the views in that layout file (and anything it includes but that's more advanced) - right now you only have one view with an id, settings_button. That's why settingsButton is the only property you can see in the binding file.
You can use findViewById to search the view hierarchy for Fragments' views - but that's generally a bad idea, an Activity shouldn't know anything about the Fragment's internal workings or layout. The Fragment should handle all that itself, and either pass data to its host activity directly, or put the data somewhere the Activity can access it (like a ViewModel, or in a Navigation backstack entry)
Basically put all your functionality inside the DialogFragment - you can use TestParametersBinding to inflate the dialog's layout in the same way you did in your activity, and you can access all its views in there, and handle whatever it is the dialog does. The activity should just show it, and do something with the result it sends back
You will have to create a class extending DialogFragment. There you need to set the id.

Why does findViewById returning null when searching for the RecyclerView?

Why is val rv: RecyclerView = findViewById(R.id.material_list_rv) returning null?
MainActivity
class MainActivity : AppCompatActivity() {
var adaptor: MaterialListAdaptor = MaterialListAdaptor()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState == null) {
supportFragmentManager.commit {
setReorderingAllowed(true)
add<MaterialListFragment>(R.id.fragment_container_view)
}
}
initRecyclerView()
setRecyclerViewData()
}
private fun initRecyclerView(){
val rv: RecyclerView = findViewById(R.id.material_list_rv)
rv.layoutManager = LinearLayoutManager(this#MainActivity)
rv.adapter = adaptor
}
private fun setRecyclerViewData(){
val l = DataSource.createMaterialList()
adaptor.setDataSet(l)
}
}
activity_main
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MaterialListFragment
class MaterialListFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_material_list, container, false)
}
}
fragment_material_list
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MaterialListFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/material_list_rv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="#layout/material_list_rv_list_item" />
</androidx.constraintlayout.widget.ConstraintLayout>
The problem
You placed the call to findViewById in MainActivity.onCreate. It's too early for that, the Fragment's view hasn't been created at that point.
You should be looking for the view in your MaterialListFragment. If you call findViewById in onCreateView, it will work. Like this:
val root = inflater.inflate(R.layout.fragment_material_list, container, false)
val rv = root.findViewById(R.id. material_list_rv)
// Do whatever (eg assign rv to a field in the Fragment to use it later)
return root
Some advice
Even if your code worked, try to have Activities, Fragments and custom Views manage their own children.
If the upper containers dive deep into their children, it's very easy to make mistakes (like the one here!) and you lose the ability to update internal details of Fragments and Views without breaking the containing Activity.
Activity/Fragment lifecycle chart
To orient yourself, keep this in mind (though this diagram is EXTREMELY simplified):
You are looking for RecylerView in MainActivity, where it does not exist.
Instead you should try to access it in FragmentActivity.

setOnClickListener doesn't work using Fragment

I'm trying to make a simple button that changes the fragment, it doesn't give any error, it just doesn't work.
XML code:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.follow.FollowFragment">
<ImageView
android:id="#+id/imageView4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="background"
android:scaleType="fitXY"
android:src="#drawable/background" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<Button
android:id="#+id/loginButton"
android:layout_width="250dp"
android:layout_height="60dp"
android:layout_marginTop="50dp"
android:backgroundTint="#color/btn_col"
android:text="Log In" />
</LinearLayout>
</FrameLayout>
Fragment code:
class FollowFragment : Fragment() {
companion object {
fun newInstance() = FollowFragment()
}
private lateinit var viewModel: FollowViewModel
private lateinit var loginButton: Button
private lateinit var viewFollow: View
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_follow, container, false)
viewFollow = root
loginButton = viewFollow.findViewById(R.id.loginButton)
var loginFragment = LoginFragment()
println("Hello :(")
loginButton.setOnClickListener {
println("Hello :)")
//parentFragmentManager.beginTransaction().replace(R.id.nav_host_fragment, loginFragment).commit()
}
return inflater.inflate(R.layout.fragment_follow, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(this).get(FollowViewModel::class.java)
// TODO: Use the ViewModel
}
}
Only the sad hello is printed, it seems to recognize the button, but the listener don't work. The app has a bottom navigation bar, and the screens are fragments.
By returning inflater.inflate(R.layout.fragment_follow, ...) you're inflating a second layout to be used for your fragment instead of the one that you're using to set the listener.
You should be returning root.

How to make a custom DialogFragment in Android?

I am trying to show a custom dialog from a fragment. When it is about to be displayed the screen gets darker, but no dialog is presented. I also tried to show it from the activity containing the fragment, but I get the same result.
Here is the layout for my dialog:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="239dp"
android:background="#00f"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="210dp"
android:layout_marginStart="20dp"
android:layout_marginLeft="20dp"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:background="#drawable/pop_up_shape"
android:backgroundTint="#00ff00"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
This is my dialog fragment class:
class AddPlaylistPopUp: DialogFragment(){
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.add_playlist_popup_layout, container, true)
}
}
and this is the method that should open the dialog in the activity:
fun testBut(view: View) {
val fm: FragmentManager = supportFragmentManager
val editNameDialogFragment = AddPlaylistPopUp()
editNameDialogFragment.show(fm, "fragment_edit_name")
}
in the fragment I tried like this:
fun openDialog(){
val fm = fragmentManager!!
val editNameDialogFragment = AddPlaylistPopUp()
editNameDialogFragment.show(fm, "fragment_edit_name")
}
This is the guide I followed:
https://guides.codepath.com/android/Using-DialogFragment#passing-data-to-parent-fragment
You are not creating any Dialog, you should be overriding onCreateDialog instead.
class AddPlaylistPopUp: DialogFragment(){
override fun onCreateDialog(
savedInstanceState: Bundle?
): Dialog {
val builder = AlertDialog.Builder(getContext())
builder.setTitle("Title")
builder.setView(R.layout.add_playlist_popup_layout)
return builder.create()
}
}
This way you don't event need to implement onCreateView(LayoutInflater, ViewGroup, Bundle) and you have much more control on the dialog creation.
If you would like to stick with onCreateView approach, you could fix your issue by changing the third parameter of function call LayoutInflater.inflate(resource, root, attachToRoot) by setting it to false.
EDIT:
To keep match_parent width, override Dialog size.

BottomSheetDialogFragment is not showing up

I've followed this tutorial to implement BottomSheetDiaogFragment in my android application.
this is my bottom sheet layout (bottom_sheet.xml):
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioGroup
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<RadioButton
android:id="#+id/rb1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:text="#string/rb1" />
<RadioButton
android:id="#+id/rb2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:text="#string/rb2" />
</RadioGroup>
</android.support.constraint.ConstraintLayout>
BottomSheetDialogFragment class:
class BottomSheetTaskRepeat : BottomSheetDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.bottom_sheet, container, false)
}
}
activity:
private val bottomSheetTaskRepeat = BottomSheetTaskRepeat()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bottomSheetTaskRepeat.show(supportFragmentManager, "my_bottom_sheet")
}
The problem is that the bottom sheet is not showing up! Any help is appreciated.
This is a late Answer, I am writing for anyone who will face the same problem, This is what I found:
For some reasons non-constrained views heights do not work in BottomSheetDialogFragment. A view whose height is like wrap_content will not show.(But the shadow will be there), but when you specify its height like 80dp it works.
For this question go and change your RadioGroup height and specify it like to:
android:layout_height="200dp"
Hope that is helpful.
UPDATE:
Since the default container of BottomSheetDailogFragment is FrameLayout and is set to WRAP_CONTENT, You can override that on your Fragment onStart method like this (Kotlin):
override fun onStart() {
super.onStart()
val containerID = com.google.android.material.R.id.design_bottom_sheet
val bottomSheet: FrameLayout? = dialog?.findViewById(containerID)
bottomSheet?.let {
BottomSheetBehavior.from<FrameLayout?>(it).state =
BottomSheetBehavior.STATE_HALF_EXPANDED
bottomSheet.layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT
}
view?.post {
val params = (view?.parent as View).layoutParams as (CoordinatorLayout.LayoutParams)
val behavior = params.behavior
val bottomSheetBehavior = behavior as (BottomSheetBehavior)
bottomSheetBehavior.peekHeight = view?.measuredHeight ?: 0
(bottomSheet?.parent as? View)?.setBackgroundColor(Color.TRANSPARENT)
}
}
Try following:
Create and use the parameterless static method newInstance in your BottomSheetDialogFragment and call the show() method on it.
Try LinearLayoutCompat as a root layout instead of ConstraintLayout.
Try a colourful background to the root layout to get an idea.
Try match_parent height for the root layout.
Make sure that the dismiss() or cancel() is not being called on it immediately.
Check visibility.
If it contains a recyclerView, make sure that it has items, getItemCount is not returning 0, and that we are setting up the values properly!
Restart both: PC and Device if your changes are not reflecting because of Android Studio errors.
Instead of using supportFragmentManager you have to use childFragmentManager,if you are inflating from inner fragments.
private val bottomSheetTaskRepeat = BottomSheetTaskRepeat()
bottomSheetTaskRepeat.show(childFragmentManager, "my_bottom_sheet")

Categories

Resources