263

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?

dark_ruby
  • 7,646
  • 7
  • 32
  • 57

16 Answers16

458

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;
}
Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
hdioui abdeljalil
  • 4,645
  • 1
  • 14
  • 4
97

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();
}

(MainFragmentBinding is automatically generated for R.layout.main_fragment)

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.

Till - Appviewer.io
  • 4,529
  • 1
  • 31
  • 35
  • Unfortunately this is killing me with the `cannot be resolved to a type` error on build. It is not reliable in my opinion. If I go first with `DataBindingUtil.inflate(inflater, R.layout.fragment_camera, container, false);` and then change it to `FragmentCameraBinding.inflate(inflater, container, false);`, it works, but after rebuild it gives the error again. – Alex Burdusel Mar 16 '17 at 18:02
  • Works great. Actually no need to specify layout res id (which i was wondering before) as it automatically picks from the generated binding file. – eC Droid Nov 30 '17 at 05:59
  • 2
    where do you set the fragment layout id (eg. R.layout.fragment_) in this example? – Raj May 06 '18 at 01:34
  • this should be the accepted answer. the layout generated binding is encouraged to be used, instead of `DataBindingUtil.inflate` – mochadwi Jan 10 '20 at 02:14
  • 1
    @LeninRajRajasekaran The layout id is implied through the use of the `MainFragmentBinding` class. That class is generated from the layout file so the desired layout is automatically applied. – Emil S. May 25 '20 at 12:42
  • However, if you look at the generated class they have marked the generated overload inflate function as deprecated and pointing to DataBindingUtil.inflate(). /** * This method receives DataBindingComponent instance as type Object instead of * type DataBindingComponent to avoid causing too many compilation errors if * compilation fails for another reason. * https://issuetracker.google.com/issues/116541301 * @Deprecated Use DataBindingUtil.inflate(inflater, R.layout.fragment_name, root, attachToRoot, component) */ – Alex Mar 31 '23 at 11:52
32

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
}
Saman Sattari
  • 3,322
  • 6
  • 30
  • 46
14

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();
}
Lefty
  • 1,192
  • 13
  • 18
10

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;
}
QoP
  • 27,388
  • 16
  • 74
  • 74
10

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
}
muneikh
  • 2,067
  • 5
  • 25
  • 59
7

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;

}
Konrad Krakowiak
  • 12,285
  • 11
  • 58
  • 45
6

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);

    }
}
vivek yadav
  • 1,367
  • 2
  • 12
  • 16
5

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();
}
UJWAL GHONGADE
  • 109
  • 1
  • 9
4

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.

Amal Sunil
  • 133
  • 2
3

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
}
BabyishTank
  • 1,329
  • 3
  • 18
  • 39
2

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.

akohout
  • 1,802
  • 3
  • 23
  • 42
2

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
}
Dev Soni
  • 489
  • 4
  • 12
2

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
    }
Ronik Limbani
  • 667
  • 4
  • 5
1

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.

Tura
  • 1,227
  • 11
  • 22
0

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 
    }
Ahmet B.
  • 1,290
  • 10
  • 20