How to use data-binding with Fragment

AndroidAndroid FragmentsAndroid Databinding

Android Problem Overview


I'm trying to follow data-binding example from official google doc https://developer.android.com/tools/data-binding/guide.html

except that I'm trying to apply data-biding to a fragment, not an activity.

the error I'm currently getting when compiling is

Error:(37, 27) No resource type specified (at 'text' with value '@{marsdata.martianSols}.

onCreate for fragment looks like this:

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    MartianDataBinding binding = MartianDataBinding.inflate(getActivity().getLayoutInflater());
    binding.setMarsdata(this);
}

onCreateView for fragment looks like this:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    return inflater.inflate(R.layout.martian_data, container, false);
}

and parts of my layout file for fragment looks like this:

<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="marsdata"
            type="uk.co.darkruby.app.myapp.MarsDataProvider" />
    </data>
...

        <TextView
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:text="@{marsdata.martianSols}"
        />

    </RelativeLayout>
</layout>

my suspicion is that MartianDataBinding doesn't know which layout file it's supposed to be bound with - hence the error. Any suggestions?

Android Solutions


Solution 1 - Android

The data binding implementation must be in the onCreateView method of the fragment, delete any data Binding that exist in your OnCreate method, your onCreateView should look like this:

public View onCreateView(LayoutInflater inflater, 
                         @Nullable ViewGroup container, 
                         @Nullable Bundle savedInstanceState) {
    MartianDataBinding binding = DataBindingUtil.inflate(
            inflater, R.layout.martian_data, container, false);
    View view = binding.getRoot();
    //here data must be an instance of the class MarsDataProvider
    binding.setMarsdata(data);
    return view;
}

Solution 2 - Android

You are actually encouraged to use the inflate method of your generated Binding and not the DataBindingUtil:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    MainFragmentBinding binding = MainFragmentBinding.inflate(inflater, container, false);
    //set variables in Binding
    return binding.getRoot();
}

Docs for DataBindingUtil.inflate():

> Use this version only if layoutId is unknown in advance. Otherwise, use the generated Binding's inflate method to ensure type-safe inflation.

Solution 3 - Android

Even the other answers may work well, but I want tell best approach.

One option is to inflate by DataBindingUtil but when only you don't know have generated binding class.

--You have auto generated binding class, use that class instead of using DataBindingUtil.

In Java

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    HomeFragmentBinding binding = HomeFragmentBinding.inflate(inflater, container, false);
    //set binding variables here
    return binding.getRoot();
}

In Kotlin

lateinit var binding: HomeFragmentBinding 
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    binding = HomeFragmentBinding.inflate(inflater, container, false)
    return binding.root
}

In DataBindingUtil class documentation you can see.

> ### inflate > > T inflate (LayoutInflater inflater, > int layoutId, > ViewGroup parent, > boolean attachToParent) > > Use this version only if layoutId is unknown in advance. Otherwise, use the generated Binding's inflate method to ensure type-safe > inflation.

If your layout biniding class is not generated @See this answer.

Solution 4 - Android

If you are using ViewModel and LiveData This is the sufficient syntax

Kotlin Syntax:

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    return MartianDataBinding.inflate(
        inflater,
        container,
        false
    ).apply {
        lifecycleOwner = viewLifecycleOwner
        vm = viewModel    // Attach your view model here
    }.root
}

Solution 5 - Android

Just as most have said, but dont forget to set LifeCycleOwner
Sample in Java i.e

public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
	super.onCreateView(inflater, container, savedInstanceState);
	BindingClass binding = DataBindingUtil.inflate(inflater, R.layout.fragment_layout, container, false);
	ModelClass model = ViewModelProviders.of(getActivity()).get(ViewModelClass.class);
	binding.setLifecycleOwner(getActivity());
	binding.setViewmodelclass(model);

	//Your codes here

	return binding.getRoot();
}

Solution 6 - Android

Try this in Android DataBinding

FragmentMainBinding binding;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main, container, false);
        View rootView = binding.getRoot();
        initInstances(savedInstanceState);
        return rootView;
}

Solution 7 - Android

Kotlin syntax:

lateinit var binding: MartianDataBinding
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    binding = DataBindingUtil.inflate(inflater, R.layout.martian_data, container, false)
    return binding.root
}

Solution 8 - Android

One can simply retrieve view object as mentioned below

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

View view = DataBindingUtil.inflate(inflater, R.layout.layout_file, container, false).getRoot();

return view;

}

Solution 9 - Android

A complete example in data binding Fragments

FragmentMyProgramsBinding is binding class generated for res/layout/fragment_my_programs

public class MyPrograms extends Fragment {
	FragmentMyProgramsBinding fragmentMyProgramsBinding;

	public MyPrograms() {
		// Required empty public constructor
	}


	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
							 Bundle savedInstanceState) {
		// Inflate the layout for this fragment
	FragmentMyProgramsBinding    fragmentMyProgramsBinding = DataBindingUtil.inflate(inflater, R
				.layout.fragment_my_programs, container, false);
		return fragmentMyProgramsBinding.getRoot();
	}

	@Override
	public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
		super.onViewCreated(view, savedInstanceState);

	}
}

Solution 10 - Android

working in my code.

private FragmentSampleBinding dataBiding;
private SampleListAdapter mAdapter;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    dataBiding = DataBindingUtil.inflate(inflater, R.layout.fragment_sample, null, false);
    return mView = dataBiding.getRoot();
}

Solution 11 - Android

I have been finding Answer for my application and here is the answer for Kotlin Language.


private lateinit var binding: FragmentForgetPasswordBinding

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?): View? {
    binding=DataBindingUtil.inflate(inflater,R.layout.fragment_forget_password,container,false)
    
    val viewModel=ViewModelProvider(this).get(ForgetPasswordViewModel::class.java)
    binding.recoveryViewModel=viewModel
    viewModel.forgetPasswordInterface=this
    return binding.root
}

Solution 12 - Android

Another example in Kotlin:

override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val binding = DataBindingUtil
            .inflate< MartianDataBinding >(
                    inflater,
                    R.layout.bla,
                    container,
                    false
            )
    
    binding.modelName = // ..
    
    return binding.root
}

Note that the name "MartianDataBinding" depends on the name of the layout file. If the file is named "martian_data" then the correct name would be MartianDataBinding.

Solution 13 - Android

Very helpful blog about Databinding : https://link.medium.com/HQY2VizKO1

class FragmentBinding<out T : ViewDataBinding>(
    @LayoutRes private val resId: Int
) : ReadOnlyProperty<Fragment, T> {

    private var binding: T? = null

    override operator fun getValue(
        thisRef: Fragment,
        property: KProperty<*>
    ): T = binding ?: createBinding(thisRef).also { binding = it }

    private fun createBinding(
        activity: Fragment
    ): T = DataBindingUtil.inflate(LayoutInflater.from(activity.context),resId,null,true)
}

Declare binding val like this in Fragment :

private val binding by FragmentBinding<FragmentLoginBinding>(R.layout.fragment_login)

Don't forget to write this in fragment

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    return binding.root
}

Solution 14 - Android

This is how you can do it in kotlin:

//Pass the layout as parameter to the fragment constructor    
class SecondFragment : Fragment(R.layout.fragment_second) {

    private var _binding: FragmentSecondBinding? = null
    private val binding  get() = _binding!!

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        _binding = FragmentSecondBinding.bind(view)  //if the view is already inflated then we can just bind it to view binding.

    }

    //Note: Fragments outlive their views. Make sure you clean up any references to the binding class
    // instance in the fragment's onDestroyView() method.
    override fun onDestroyView() {
        Toast.makeText(activity, "On destroy", Toast.LENGTH_SHORT).show()
        super.onDestroyView()
        _binding = null
    }
}

You can access the view elements from your layouts like:

binding.tvName.text = "Messi"

where tvName is the id of the view element.

Solution 15 - Android

Everyone says about inflate(), but what if we want to use it in onViewCreated()?

You can use bind(view) method of concrete binding class to get ViewDataBinding instance for the view.


Usually we write BaseFragment something like this (simplified):

// BaseFragment.kt
abstract fun layoutId(): Int

override fun onCreateView(inflater, container, savedInstanceState) = 
    inflater.inflate(layoutId(), container, false)

And use it in child fragment.

// ConcreteFragment.kt
override fun layoutId() = R.layout.fragment_concrete

override fun onViewCreated(view, savedInstanceState) {
    val binding = FragmentConcreteBinding.bind(view)
    // or
    val binding = DataBindingUtil.bind<FragmentConcreteBinding>(view)
}


If all Fragments uses data binding, you can even make it simpler using type parameter.

abstract class BaseFragment<B: ViewDataBinding> : Fragment() {
    abstract fun onViewCreated(binding: B, savedInstanceState: Bundle?)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        onViewCreated(DataBindingUtil.bind<B>(view)!!, savedInstanceState)
    }
}

I don't know it's okay to assert non-null there, but.. you get the idea. If you want it to be nullable, you can do it.

Solution 16 - Android

Kotlin

         override fun onCreateView(
                 inflater: LayoutInflater, container: ViewGroup?,
                 savedInstanceState: Bundle?
            ): View? 
    { 
                 val binding = FragmentFirstBinding.inflate(inflater,container,false)      
               return  binding.root;
            

}

where FragmentFirstBinding is Automatic generated by android studio using view binding. In my code fragment name is FirstFragment.

First, you need to add this line to your build.Gradle(app module) file.

buildFeatures{
        viewBinding true
    }

Solution 17 - Android

Shortest way in Kotlin;

class HomeFragment : Fragment(R.layout.fragment_home) {


override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val binding = FragmentHomeBinding.bind(view)
    // todo 
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questiondark_rubyView Question on Stackoverflow
Solution 1 - Androidhdioui abdeljalilView Answer on Stackoverflow
Solution 2 - AndroidTillView Answer on Stackoverflow
Solution 3 - AndroidKhemraj SharmaView Answer on Stackoverflow
Solution 4 - AndroidSaman SattariView Answer on Stackoverflow
Solution 5 - AndroidLeftyView Answer on Stackoverflow
Solution 6 - AndroidJirawat HarnsiriwatanakitView Answer on Stackoverflow
Solution 7 - AndroidmuneikhView Answer on Stackoverflow
Solution 8 - AndroidImran Solanki - GSLabView Answer on Stackoverflow
Solution 9 - Androidvivek yadavView Answer on Stackoverflow
Solution 10 - AndroidUJWAL GHONGADEView Answer on Stackoverflow
Solution 11 - AndroidBarkat AnsariView Answer on Stackoverflow
Solution 12 - AndroidakohoutView Answer on Stackoverflow
Solution 13 - AndroidDev SoniView Answer on Stackoverflow
Solution 14 - AndroidAmal SunilView Answer on Stackoverflow
Solution 15 - AndroidTuraView Answer on Stackoverflow
Solution 16 - AndroidRonikView Answer on Stackoverflow
Solution 17 - AndroidAhmet B.View Answer on Stackoverflow