cancel
Showing results for 
Search instead for 
Did you mean: 

Object Header Data Binding

0 Kudos

Hi SAP Team,

I am trying to use databinding in conjunction with with the Object Header component, but it just partially works for me.
I can bind data to headline/subheadline/footnote etc., but as soon as I try to bind data to the status TextViews I added to the Object Header, I get the following Nullpointer Exception:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.apsolut.inbox.demo, PID: 8383
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.apsolut.inbox.demo/com.apsolut.inbox.ui.PRItemDetailActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setTag(java.lang.Object)' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setTag(java.lang.Object)' on a null object reference
        at com.apsolut.inbox.databinding.ItemDetailObjectHeaderBindingImpl.<init>(ItemDetailObjectHeaderBindingImpl.java:34)
        at com.apsolut.inbox.databinding.ItemDetailObjectHeaderBindingImpl.<init>(ItemDetailObjectHeaderBindingImpl.java:25)
        at com.apsolut.inbox.DataBinderMapperImpl.getDataBinder(DataBinderMapperImpl.java:44)
        at androidx.databinding.MergedDataBinderMapper.getDataBinder(MergedDataBinderMapper.java:74)
        at androidx.databinding.DataBindingUtil.bind(DataBindingUtil.java:199)
        at androidx.databinding.ViewDataBinding.mapBindings(ViewDataBinding.java:1250)
        at androidx.databinding.ViewDataBinding.mapBindings(ViewDataBinding.java:1266)
        at androidx.databinding.ViewDataBinding.mapBindings(ViewDataBinding.java:1266)
        at androidx.databinding.ViewDataBinding.mapBindings(ViewDataBinding.java:1266)
        at androidx.databinding.ViewDataBinding.mapBindings(ViewDataBinding.java:717)
        at com.apsolut.inbox.databinding.ItemPrDetailBindingImpl.<init>(ItemPrDetailBindingImpl.java:39)
        at com.apsolut.inbox.DataBinderMapperImpl.getDataBinder(DataBinderMapperImpl.java:50)
        at androidx.databinding.MergedDataBinderMapper.getDataBinder(MergedDataBinderMapper.java:74)
        at androidx.databinding.DataBindingUtil.bind(DataBindingUtil.java:199)
        at androidx.databinding.DataBindingUtil.bindToAddedViews(DataBindingUtil.java:327)
        at androidx.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:306)
        at androidx.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:284)
        at com.apsolut.inbox.ui.PRItemDetailActivity.onCreate(PRItemDetailActivity.java:28)
        at android.app.Activity.performCreate(Activity.java:7136)
        at android.app.Activity.performCreate(Activity.java:7127)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6669) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 


XML Object Header (This is an include file which works fine as long as I do not try to bind data to the Textviews):

<layout>
    <data>
        <variable
            name="bodyText"
            type="String" />
        <variable
            name="footnoteText"
            type="String" />
        <variable
            name="headlineText"
            type="String" />
        <variable
            name="subheadlineText"
            type="String" />
        <variable
            name="statusText1"
            type="String" />
        <variable
            name="statusText2"
            type="String" />
        <variable
            name="descriptionText"
            type="String" />
    </data>
    <com.sap.cloud.mobile.fiori.object.ObjectHeader xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/detail_object_header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:body="@{bodyText ?? ``}"
        app:dynamicStatusWidth="true"
        app:footnote="@{footnoteText ?? ``}"
        app:headline="@{headlineText ?? ``}"
        app:description="@{descriptionText ?? ``}"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:preserveDescriptionSpacing="false"
        app:preserveDetailImageSpacing="false"
        app:preserveIconImageContainer="false"
        app:preserveIconStackSpacing="false"
        app:subheadline="@{subheadlineText ?? ``}">

        <TextView
            android:id="@+id/object_header_status"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:text="@{statusText1 ?? ``}"
            android:textColor="@color/white"
            app:layout_header_group="STATUS" />

        <TextView
            android:id="@+id/object_header_prio"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:paddingTop="5dp"
            android:text="@{statusText2 ?? ``}"
            android:textColor="@color/white"
            app:layout_header_group="STATUS" />
    </com.sap.cloud.mobile.fiori.object.ObjectHeader>
</layout>

Is this a bug within the component or do I have to apply the data through the Object Header instead of directly referencing the TextViews ?

Setting the data programmatically like this works for example:

objectHeader.setStatus("Status 1", 0);
objectHeader.setStatus("Status 2", 1);

Hope you can help me out.

Regards

Eduard

Accepted Solutions (0)

Answers (1)

Answers (1)

0 Kudos

Hi Eduard,

The XML looks fine. The error message is complaining about setTag, which I cannot find in ObjectHeader or AbstractEntityCell. Can you double check where the `setTag` call comes from?

Thanks,

Hui Cao

0 Kudos

Hi Hui,

The setTag call comes from ItemDetailObjectHeaderBindingImpl class which is generated by Android Studio based on the wrapped around <layout>-Tag where my ObjectHeader is located (see XML). The setTag() call is a method from class View which is being called in the generated Android binding class.

The error occurs in the second constructor referencing TextView with the id "object_header_prio" when Android tries to call this.objectHeaderPrio.setTag(null);

public ItemDetailObjectHeaderBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
    this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
}
private ItemDetailObjectHeaderBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
    super(bindingComponent, root, 0
        , (com.sap.cloud.mobile.fiori.object.ObjectHeader) bindings[0]
        , (android.widget.TextView) bindings[2]
        , (android.widget.TextView) bindings[1]
        );
    this.detailObjectHeader.setTag(null);
    this.objectHeaderPrio.setTag(null);
    this.objectHeaderStatus.setTag(null);
    setRootTag(root);
    // listeners
    invalidateAll();
}

It seems like the databinding implementation can not find the TextView implemented within the ObjectHeader or at least can not reference to the XML-View for some reason. Therefore this.objectHeaderPrio and this.objectHeaderStatus are being referenced as null which results in the displayed NullpointerException stacktrace, because we can not call setTag() on null.

But why is it not possible for Android to find the TextView within the ObjectHeader ?

I had a similiar issue directly referencing the TextViews by calling findViewById() for example and you are only able to receive those references by calling them like this through the generated object header object:

objectHeader.setStatus("Status 1", 0);
objectHeader.setStatus("Status 2", 1);
Maybe these issues are somehow related
0 Kudos

Hi Eduard,

Now I see where the setTag call comes from. In our demo app, we also have a DataBinding sample activity, which is using ObjectCell, instead of ObjectHeader. But both of them are sharing the same logic in terms of status field. Once I added @+id/status_image for the status field in xml, I was able to see the reference to it in the generated code: this.statusImage.setTag(null);but it works fine in my environment. So what we can do is: 1. try to remove id and see whether it works around the issue; 2. can you share a sample project that can reproduce the issue?

Thanks,

Hui Cao

0 Kudos

Hi Hui,

yes, it works fine with the Object cell component and I know the Object Header just inherits from Object Cell. I should have mentioned this before, sorry. But try to change to an Object Header and test it. I guarantee you it will not work. I don't know why, but something has to be different.

Regards

Eduard

0 Kudos

Hi Eduard,

I reused your xml layout and created a simple Activity with the following code and it works fine. If you could share your files that'll make it easier to debug.

Thanks,

Hui Cao

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ItemDetailBinding binding = ItemDetailBinding.inflate(getLayoutInflater());
    setContentView(binding.getRoot());
    binding.setHeadlineText("Headline");
    binding.setSubheadlineText("Subheadline");
    binding.setStatusText1("Status1");
    binding.setStatusText2("Status2");
}
0 Kudos

Hi Eduard,

Now I'm able to reproduce the issue on a phone or tablet portrait mode. Previously I was testing on tablet landscape mode. The ObjectHeader uses ViewPager2 on phone or tablet portrait mode to layout child views, which in turn uses a RecyclerView to manage pages. RecyclerView does not add child views until it is laid out. While the data binding library tries to access the TextView right after ObjectHeader is constructed, which hasn't been laid out yet, therefore the reference becomes null.

At this moment, I don't have any workarounds for this issue. Please avoid using xml child views and data binding together for ObjectHeader.

Thanks,

Hui Cao

0 Kudos

Hi Hui,

thank you for the explanation

Regards,
Eduard