DataBinding fails on custom landscape layout inflation

Viewed 1982

I created a alternative layout XML file for landscape orientation. The inflation using the DataBindingUtil works fine in portrait mode but crashes in landscape mode when the custom XML file is loaded.

Layout inflation using DataBinding:

class AskhostingFragment : Fragment() {

    private lateinit var binding : FragmentAskhostingBinding

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_askhosting, container, false)

        return binding.root
    }
}

I include both XML files although I suspect they are relevant to the issue. layout/fragment_askhosting.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/askhosting_cl_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/askhosting_cl_1"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="@color/colorPrimaryDark"
            app:layout_constraintBottom_toTopOf="@+id/askhosting_cl_2"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <TextView
                android:id="@+id/textView3"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="64dp"
                android:layout_marginTop="64dp"
                android:layout_marginEnd="64dp"
                android:text="@string/askhosting_option_stundenplan24"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                style="@style/TextLargeMultiline" />

            <Button
                android:id="@+id/button3"
                style="@style/Widget.AppCompat.Button.Colored"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:layout_marginEnd="32dp"
                android:layout_marginBottom="32dp"
                android:text="@string/stundenplan24"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/textView3"
                app:layout_constraintVertical_bias="1.0" />
        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/askhosting_cl_2"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="@color/colorPrimary"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/askhosting_cl_1">

            <ImageView
                android:id="@+id/imageView2"
                android:layout_width="110dp"
                android:layout_height="110dp"
                android:layout_marginTop="32dp"
                android:layout_marginEnd="32dp"
                android:contentDescription="@string/school_website"
                android:tint="@color/colorPrimaryDark"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/ic_web_black" />

            <TextView
                android:id="@+id/textView4"
                style="@style/TextLargeMultiline"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="64dp"
                android:layout_marginTop="16dp"
                android:layout_marginEnd="64dp"
                android:text="@string/askhosting_option_website"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.0"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/imageView2" />

            <Button
                android:id="@+id/button4"
                style="@style/Widget.AppCompat.Button.Colored"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:layout_marginEnd="32dp"
                android:layout_marginBottom="32dp"
                android:text="@string/school_website"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/textView4"
                app:layout_constraintVertical_bias="1.0" />
        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

layout-land/fragment_askhosting.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/askhosting_cl_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/askhosting_cl_1"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="@color/colorPrimaryDark"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/askhosting_cl_2"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <TextView
                android:id="@+id/textView3"
                style="@style/TextLargeMultiline"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="64dp"
                android:layout_marginTop="64dp"
                android:layout_marginEnd="64dp"
                android:text="@string/askhosting_option_stundenplan24"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <Button
                android:id="@+id/button3"
                style="@style/Widget.AppCompat.Button.Colored"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:layout_marginEnd="32dp"
                android:layout_marginBottom="32dp"
                android:text="@string/stundenplan24"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/textView3"
                app:layout_constraintVertical_bias="1.0" />
        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/askhosting_cl_2"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="@color/colorPrimary"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toEndOf="@+id/askhosting_cl_1"
            app:layout_constraintTop_toTopOf="parent">

            <ImageView
                android:id="@+id/imageView2"
                android:layout_width="110dp"
                android:layout_height="110dp"
                android:layout_marginTop="32dp"
                android:layout_marginEnd="32dp"
                android:contentDescription="@string/school_website"
                android:tint="@color/colorPrimaryDark"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/ic_web_black" />

            <TextView
                android:id="@+id/textView4"
                style="@style/TextLargeMultiline"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="64dp"
                android:layout_marginTop="16dp"
                android:layout_marginEnd="64dp"
                android:text="@string/askhosting_option_website"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.0"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/imageView2" />

            <Button
                android:id="@+id/button4"
                style="@style/Widget.AppCompat.Button.Colored"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:layout_marginEnd="32dp"
                android:layout_marginBottom="32dp"
                android:text="@string/school_website"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/textView4"
                app:layout_constraintVertical_bias="1.0" />
        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

The error message when turning the device to landscape mode (or starting the app in landscape orientation):

java.lang.IllegalArgumentException: The tag for fragment_askhosting is invalid. Received: layout-land/fragment_askhosting_0
        at net.hermlon.gcgtimetable.DataBinderMapperImpl.getDataBinder(DataBinderMapperImpl.java:42)
        at androidx.databinding.MergedDataBinderMapper.getDataBinder(MergedDataBinderMapper.java:74)
        at androidx.databinding.DataBindingUtil.bind(DataBindingUtil.java:199)
        at androidx.databinding.DataBindingUtil.inflate(DataBindingUtil.java:130)
        at androidx.databinding.DataBindingUtil.inflate(DataBindingUtil.java:95)
        at net.hermlon.gcgtimetable.ui.profile.create.AskhostingFragment.onCreateView(AskhostingFragment.kt:21)
        at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2600)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:881)
        at androidx.fragment.app.FragmentManagerImpl.addAddedFragments(FragmentManagerImpl.java:2100)
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1874)
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1830)
        at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
        at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2663)
        at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2613)
        at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:2624)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:904)
        at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
        at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2659)
        at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2613)
        at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:246)
        at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:542)
        at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:201)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1425)
        at android.app.Activity.performStart(Activity.java:7825)
        at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3294)
        at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
        at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

The code that throws the exception is the following function in the generated file app/build/generated/source/kapt/debug/net/hermlon/gcgtimetable/DataBinderMapperImpl.java:

@Override
  public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
    int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
    if(localizedLayoutId > 0) {
      final Object tag = view.getTag();
      if(tag == null) {
        throw new RuntimeException("view must have a tag");
      }
      switch(localizedLayoutId) {
        case  LAYOUT_FRAGMENTASKHOSTING: {
          if ("layout/fragment_askhosting_0".equals(tag)) {
            return new FragmentAskhostingBindingImpl(component, view);
          }
          throw new IllegalArgumentException("The tag for fragment_askhosting is invalid. Received: " + tag);
        }
      }
    }
    return null;
  }

I guess DataBinding has a problem when the tag (whatever this is) of the layout it gets is changed due to the inflation of the landscape layout. Is this a bug or is DataBinding just not capable of dealing with two changeable layout files?

Update: It seems like this would be a known issue for some time: https://issuetracker.google.com/issues/62740925 And for some people multiple layouts seems to work fine: Android databinding with multiple layouts

1 Answers

Solved: DataBinding works perfectly with landscape layouts, I just had to rebuild the app (Build->Rebuild Project). Now the generated code looks like this and doesn't throw the exception anymore:

@Override
  public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
    int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
    if(localizedLayoutId > 0) {
      final Object tag = view.getTag();
      if(tag == null) {
        throw new RuntimeException("view must have a tag");
      }
      switch(localizedLayoutId) {
        case  LAYOUT_FRAGMENTASKHOSTING: {
          if ("layout/fragment_askhosting_0".equals(tag)) {
            return new FragmentAskhostingBindingImpl(component, view);
          }
          if ("layout-land/fragment_askhosting_0".equals(tag)) {
            return new FragmentAskhostingBindingLandImpl(component, view);
          }
          throw new IllegalArgumentException("The tag for fragment_askhosting is invalid. Received: " + tag);
        }
      }
    }
    return null;
  }
Related