Get binding from view class

2019-03-27 06:45发布

问题:

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.

回答1:

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.



回答2:

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.