Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get binding from view class

I have a CustomView class with a databound layout that takes a variable. In the layout that contains the CustomView, I want to pass an attribute into the CustomView, and have that CustomView pass that attribute into its own layout's binding. Here's what I have:

public class CustomView extends LinearLayout
{
public CustomView(Context inContext, AttributeSet inAttrs)
{
    super(inContext, inAttrs);

    inflate(inContext, R.layout.custom_view, null);
}



@BindingAdapter({"app:variable"})
public static void SetVariable(CustomView inCustomView, VariableType inMyVariable)
{
    CustomViewBinding binding = DataBindingUtil.getBinding(inCustomView);

    binding.setMyVariable(inMyVariable);
}
}

This crashes trying to extract the binding from the view. Is this even possible? Here is the stack trace:

java.lang.NullPointerException: Attempt to invoke virtual method 'void xxx.databinding.CustomViewBinding.setVariableType(xxx.VariableType)' on a null object reference
                                                                            at xxx.CustomView.SetDynamicList(CustomView.java:32)
                                                                            at xxx.MyFragmentBinding.executeBindings(MyFragmentBinding.java:116)
                                                                            at android.databinding.ViewDataBinding.executePendingBindings(ViewDataBinding.java:350)
                                                                            at android.databinding.ViewDataBinding$6.run(ViewDataBinding.java:167)
                                                                            at android.databinding.ViewDataBinding$7.doFrame(ViewDataBinding.java:233)
                                                                            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:856)
                                                                            at android.view.Choreographer.doCallbacks(Choreographer.java:670)
                                                                            at android.view.Choreographer.doFrame(Choreographer.java:603)
                                                                            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
                                                                            at android.os.Handler.handleCallback(Handler.java:739)
                                                                            at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                            at android.os.Looper.loop(Looper.java:148)
                                                                            at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                            at java.lang.reflect.Method.invoke(Native Method)
                                                                            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

And if I change

DataBindingUtil.getBinding(inCustomView)

to

DataBindingUtil.bind(inCustomView)

then I get this:

java.lang.IllegalArgumentException: View is not a binding layout
                                                                            at android.databinding.DataBindingUtil.bind(DataBindingUtil.java:166)
                                                                            at android.databinding.DataBindingUtil.bind(DataBindingUtil.java:140)
                                                                            at xxx.CustomView.SetDynamicList(CustomView.java:30)
                    -                                                        at xxx.databinding.MyFragmentBinding.executeBindings(MyFragmentBinding.java:116)
                                                                            at android.databinding.ViewDataBinding.executePendingBindings(ViewDataBinding.java:350)
                                                                            at android.databinding.ViewDataBinding$6.run(ViewDataBinding.java:167)
                                                                            at android.databinding.ViewDataBinding$7.doFrame(ViewDataBinding.java:233)
                                                                            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:856)
                                                                            at android.view.Choreographer.doCallbacks(Choreographer.java:670)
                                                                            at android.view.Choreographer.doFrame(Choreographer.java:603)
                                                                            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
                                                                            at android.os.Handler.handleCallback(Handler.java:739)
                                                                            at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                            at android.os.Looper.loop(Looper.java:148)
                                                                            at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                            at java.lang.reflect.Method.invoke(Native Method)
                                                                            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

This might imply that the layout file is not formatted for databinding, but it is. It has the layout element, and the data element with variables and everything.

like image 496
KairisCharm Avatar asked Mar 16 '16 19:03

KairisCharm


People also ask

How do I use layouts in view binding?

In the include layout you must create a Container layout and put here the id. Show activity on this post. private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super. onCreate(savedInstanceState) binding = DataBindingUtil.

How would you enable view binding for a module in your application so you can reference views from layout files without using Findviewbyid?

To enable view binding, configure viewBinding in your module-level build. gradle file. Once enabled for a project, view binding will generate a binding class for all of your layouts automatically. You don't have to make changes to your XML — it'll automatically work with your existing layouts.

What is ActivityMainBinding?

The above layout filename is activity_main. xml so the corresponding generated class is ActivityMainBinding . This class holds all the bindings from the layout properties (for example, the user variable) to the layout's views and knows how to assign values for the binding expressions.


2 Answers

You must bind the inflated view to create the data binding. In your example, you're binding the container of the layout.

You can do this in several ways. The easiest is to bind it as part of inflation:

public class CustomView extends LinearLayout
{
  CustomViewBinding mBinding;
  public CustomView(Context inContext, AttributeSet inAttrs)
  {
    super(inContext, inAttrs);
    LayoutInflater inflater = LayoutInflater.from(inContext);
    // I assume you want it inflated into this ViewGroup
    mBinding = CustomViewBinding.inflate(inflater, this, true);
  }

  public void setVariable(CustomView inCustomView, VariableType inMyVariable) {
    mBinding.setVariable(inMyVariable);
  }
  ...
}

You don't really need a binding adapter unless you don't want the setter as part of your custom view. In that case, you'll still need a way to get the binding, so you'll need to add something like this:

public CustomViewBinding getBinding() { return mBinding; }

so that your binding adapter works.

If you know that the LinearLayout contents are all going to be from the inflated view, you can use a binding adapter like this:

@BindingAdapter({"app:variable"})
public static void setVariable(CustomView inCustomView, VariableType inMyVariable)
{
    if (inCustomView.getChildCount() == 0) {
      return;
    }
    View boundView = inCustomView.getChildAt(0);
    CustomViewBinding binding = DataBindingUtil.getBinding(boundView);

    binding.setMyVariable(inMyVariable);
}

If your custom view isn't very custom, you can simply include your layout directly:

<include layout="@layout/custom_view" app:variable="@{myVariableValue}"/>

You would, of course, have to move the LinearLayout into the custom_view.xml.

like image 159
George Mount Avatar answered Oct 25 '22 16:10

George Mount


Basically, this is how it worked for me:

XML:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/containerView"
xmlns:tools="http://schemas.android.com/tools">

.....


</layout>

Java Class:

GeneralOverviewBinding binding = DataBindingUtil.bind((findViewById(R.id.containerView)));

This basically returns the binding for the given layout root or creates a binding if one does not exist.

like image 29
Sindhu Avatar answered Oct 25 '22 15:10

Sindhu