I have a string variable in my fragment which I want to use in XML with data binding
This is my fragment
class HomeFragment : Fragment() {
var struc: String = "Organization Structure"
lateinit var binding: FragmentHomeBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.getRoot()
}}
This is the 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="data"
type="com.mountmeru.view.HomeFragment" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.HomeFragment">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvOrganization"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/ivOrganization"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="#{data.struc}"
android:textAlignment="center"
android:textColor="#color/mountmerured"
android:textSize="15sp" />
</FrameLayout></layout>
When I run the application the textview text is never set.
What is wrong here?
First way :
This not direct way which you asked. But you can also do using data class, this is little bit long way but you can add multiple data.
Make data class
data class Data(var struc: String = "Organization Structure")
Add this in xml
<data>
<variable
name="dataModel"
type="Data" /> // here data class path of step1
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.HomeFragment">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvOrganization"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/ivOrganization"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="#{dataModel.struc}" // use like this
android:textAlignment="center"
android:textColor="#color/mountmerured"
android:textSize="15sp" />
</FrameLayout></layout>
In Activity or fragment
private fun setData() {
val data = Data(struc= "Organization")
bindObject.dataModel = data
}
Second way :
Or Else direct way is
<data>
<variable
name="struc"
type="String" />
</data>
and from activity/fragment
bindObject.struc = "Organization Structure";
Third way :
Or In your case you need to do this in onCreateView and everything works
binding.data = this;
I think you have mistaken while the layout inflating. Please try below code
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = DataBindingUtil.inflate(inflater, layoutId, container, false)
return binding.getRoot()
}}
Related
I've done my research on memory leak when using Data Binding. Every posts said that one should assign null to binding object to resolve this issue. However, it just doesn't work.
I've created two identical Fragments with a single Activity. Each Fragment includes a button which can be used to navigate to one another.
To reproduce this issue, click the button repeatedly, LeakCanary will pop up the memory leak warning. (However, the issue does not occur when clicking the buttons "programmatically")
Repro
LeakCanary
Code
The following is the code of my Fragments. (Two fragments are identical)
FirstFragment.kt
class FirstFragment : Fragment() {
private var _binding: FragmentFirstBinding? = null
val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
_binding = FragmentFirstBinding.inflate(layoutInflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
binding.btnFirst.setOnClickListener {
findNavController().navigate(R.id.action_to_second)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
fragment_first.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">
<data>
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/first_fragment" />
<com.google.android.material.button.MaterialButton
android:id="#+id/btn_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="To second" />
</FrameLayout>
</layout>
SecondFragment.kt
class SecondFragment : Fragment() {
private var _binding: FragmentSecondBinding? = null
val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
_binding = FragmentSecondBinding.inflate(layoutInflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
binding.btnSecond.setOnClickListener {
findNavController().navigate(R.id.action_to_first)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
fragment_second.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">
<data>
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SecondFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#string/second_fragment" />
<com.google.android.material.button.MaterialButton
android:id="#+id/btn_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="To first" />
</FrameLayout>
</layout>
I've also created a repo in case someone want to try it out.
Let me know if I can provide any further information.
Thanks!
To all whom may have run into this issue, the developer team of leakcanaray has found the source and marked the leak as known issue.
github.com/square/leakcanary/issues/2341
A related issue can be found on Google issue tracker.
I encountered a problem when using databinding and viewpager2. I used viewpager2 in a fragment. FragmentA in viewpager2 wanted to share the viewmodel of the fragment.
A TextView attribute text is bound in fragmentA, but postValue in viewmodel cannot change the text of TextView
But fragmentA is bound to a click event (TextView has a click, triggering the sendCode() function), which can be triggered in the viewmodel
fragmentA:
class SignUpMainFragment(val vm:SignUpFragmentVM):Fragment() {
private var mBinding:FragmentSignUpMainBinding?=null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_sign_up_main,container,true)
mBinding?.signUp = vm
return mBinding?.root!!
}
}
Layout of fragmentA:
<?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="signUp"
type="com.xxx.SignUpFragmentVM" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="#+id/et_email"
android:layout_width="468dp"
android:layout_height="94dp"
android:layout_marginStart="40dp"
android:hint="Email"
android:text="#={signUp.txtEmail}"
android:textSize="30sp"
android:textColor="#color/color_aaa280"
android:drawableStart="#mipmap/img_mail"
android:paddingStart="26dp"
android:drawablePadding="18dp"
android:inputType="textEmailAddress"
android:background="#drawable/shape_edit_bg_e7e7e7"
app:layout_constraintTop_toTopOf="#id/et_name"
app:layout_constraintStart_toEndOf="#id/et_name" />
<TextView
android:id="#+id/tv_send_code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{signUp.txtSendEmail}"
android:textSize="30.5sp"
android:textColor="#color/color_71cbc0"
android:onClick="#{signUp.click}"
android:clickable="true"
android:layout_marginStart="25dp"
app:layout_constraintTop_toTopOf="#id/et_lock_psw"
app:layout_constraintBottom_toBottomOf="#id/et_lock_psw"
app:layout_constraintStart_toEndOf="#id/et_lock_psw"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
SignUpFragmentVM:
class SignUpFragmentVM constructor():ViewModel() {
val txtSendEmail = MutableLiveData("Get verified.")
....
private fun sendCode(){
viewModelScope.launch {
...
//TODO:60s Countdown
txtSendEmail.postValue("60")
}
}
}
You need more
mBinding?.signUp = vm
mBinding?.lifecycleOwner = this // => add
I've an enum class Product, that has enums listed as well as a companion object method to return a list.
#Parcelize
enum class Product: Parcelable {
FOO,
BAR,
BAZ;
companion object {
fun list(): ArrayList<Product> {
return arrayListOf(FOO, BAR, BAZ)
}
}
}
In the layout.xml I have a spinner with an import statement. I'm binding the list function with the spinner entries.
<layout>
<data>
<import type="data.Product" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout>
<Spinner
android:id="#+id/spinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:entries="#{Product.Companion.list()}" />
Problem: Can't build or compile the app.
Could not find accessor data.Product.Companion.comboList
Edit 1
I got one step closer with the help of this article.
Changed import to a variable and included Companion object
<data>
<variable name="productStatic" type="data.Product.Companion" />
</data>
and spinner as
<Spinner
android:id="#+id/spinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:entries="#{ProductStatic.list}" />
FormFragment is as (I'm not clear here, how to handle here?), spinner list is still empty.
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = UserFormBinding.inflate(inflater, container, false).apply {
productStatic.list = Product.list()
}
return binding.root
}
Edit 2
From the comments, I realized I was doing it wrong, it should have been through an adapter from kotlin code. Here's my update but the question is how do I auto select it?
Layout xml
<data>
<variable name="products" type="android.widget.ArrayAdapter" />
<variable name="user" type="data.User" />
</data>
...
<Spinner
android:id="#+id/spinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:adapter="#{products}" />
FormFragment.
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = UserFormBinding.inflate(inflater, container, false).apply {
user = this#UserForm.user
products = ArrayAdapter(activity!!, android.R.layout.simple_spinner_item, Product.list())
}
return binding.root
}
I am using a fragment within an activity and using data binding to bind its layout for both the activity and fragment as shown below:
activity.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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#id/FAB_fromHomeActivity_BottomAppBarAttached"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_add_dark"
android:backgroundTint="#color/colorAccent"
app:layout_anchor="#id/BottomAppBar_fromHomeActivity_Main"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
activity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = DataBindingUtil.setContentView(this, R.layout.activity)
}
fragment.kt
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_goals, container, false)
return mBinding.root
}
Is there any way to access the FAB in the activity using the binding in the fragment ? (For example something like that mBinding.parent.FAB)
I can't find any information on this. Can anyone help ?
I have an object that will fill some fields in my xml, but a mutablelivedata return a T value and I can't use the T properties. What I need to do to use the T object in my xml?
I don't want to create a mutablelivedata for each object member.
Sorry for my bad english..
class ExampleFragment: Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val viewModel = ViewModelProviders.of(this).get(MyVm::class.java)
val binding = DataBindingUtil.inflate<FragmentExampleBinding>(
inflater,
R.layout.fragment_house,
container,
false
)
binding.myVm = viewModel
binding.lifecycleOwner = this
return binding.root
}
}
class MyVm: ViewModel(){
val house: MutableLiveData<House> by lazy { MutableLiveData<House>().apply{ value =
House().apply{
door = "First door"
window = "My window"
}
} }
val thing: MutableLiveData<String> by lazy { MutableLiveData<String>().apply{ value = "something" } }
}
class House(){
var door: String = ""
var window: String = ""
}
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="my_vm" type="br.com.MyVm"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
orientation="vertical"
>
<!--I need this work-->
<TextView
android:layout_width="wrap_parent"
android:layout_height="wrap_parent"
android:text="my_vm.house.door"
/>
<!--I need this work-->
<TextView
android:layout_width="wrap_parent"
android:layout_height="wrap_parent"
android:text="my_vm.house.window"
/>
<TextView
android:layout_width="wrap_parent"
android:layout_height="wrap_parent"
android:text="my_vm.thing"
/>
</LinearLayout>
<layout>
This code is correcty! A tried run and worked, I thounght it didn't work because the android studio didn't show the attributes when press ctrl + space.