I'm trying to follow data-binding example from official google doc
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=""> <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?
17 Answers
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; } 12You 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():
5Use this version only if layoutId is unknown in advance. Otherwise, use the generated Binding's inflate method to ensure type-safe inflation.
Even the other answers may work well, but I want tell best approach.
Use Binding class's inflate as recommended in Android Documentation.
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.
4If 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 } 0Just 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(); } 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; } 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 } 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; } 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); } } 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(); } 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 } 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.
Very helpful blog about Databinding :
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 } 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.
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.
1Kotlin
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 } 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 }