How do I handle missing views in different layout configurations when using view binding?

Viewed 726

I understand that any XML elements with ID should be automatically pull into an activity class through view binding. However, Android Studio keeps suggesting that my button is a nullable and needs to have ?. or !!. for it to compile.

If I assert non-nullable with !!., it actually result in a runtime NullPointerException for the button.

Why is view binding not recognizing my button's type and existence?

My main activity code here:

package com.stevenswang.ageinminutes

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.stevenswang.ageinminutes.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.btnSelectDate.setOnClickListener {...} //this line gives the compiler error in the title
    }
    
}

XML for the app here (in its entirety):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/backgroundColor"
    android:orientation="vertical"
    android:gravity="center_horizontal"
    android:padding="16sp"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:text="Caculate Your"
        android:textColor="@color/headingColor"
        android:textSize="30sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/tvAge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:text="Age"
        android:textColor="@color/white"
        android:background="@color/purple_200"
        android:padding="10sp"
        android:textSize="30sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/tvInMinutes"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:text="In Minutes"
        android:textColor="@color/headingColor"
        android:textSize="30sp"
        android:textStyle="bold" />

    <Button
        android:id="@+id/btnSelectDate"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Select Date"
        android:layout_marginTop="10sp"
        android:textSize="20sp"
        android:backgroundTint="@color/buttonColor"
        />

    <TextView
        android:id="@+id/tvSelectedDate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:textColor="@color/headingColor"
        android:textSize="20sp"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Selected Date"
        android:layout_marginTop="2dp"
        android:textColor="@color/gray"
        android:textSize="20sp"
        />

    <TextView
        android:id="@+id/tvAgeInMinutes"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:textColor="@color/headingColor"
        android:textSize="36sp"
        android:textStyle="bold"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Age in minutes"
        android:layout_marginTop="2dp"
        android:textColor="@color/gray"
        android:textSize="24sp"
        />


</LinearLayout>

I figured out the culprit, on the view binding docs it says:

Null safety: Since view binding creates direct references to views, there's no risk of a null pointer exception due to an invalid view ID. Additionally, when a view is only present in some configurations of a layout, the field containing its reference in the binding class is marked with @Nullable

Source: https://developer.android.com/topic/libraries/view-binding#findviewbyid

One rule that I had in my button was only used in API 21 or above, and my current min is set to 16 (See screenshot below). I followed Android Studio's suggestion and override the version, thus created 2 separate activity_main.xml. This is what triggered the nullable behavior.

API restriction creating multiple xml files

When I deleted the v21 xml file, the nullable went away.

Now in my follow up questions for whoever reads this, why does asserting non-nullable still cause the app to crash? What is the correct way to handle this with multiple activity_main.xml versions?

Thanks in advance!

1 Answers

I figured out the culprit, on the view binding docs it says:

Null safety: Since view binding creates direct references to views, there's no risk of a null pointer exception due to an invalid view ID. Additionally, when a view is only present in some configurations of a layout, the field containing its reference in the binding class is marked with @Nullable

Source: https://developer.android.com/topic/libraries/view-binding#findviewbyid

One rule that I had in my button was only used in API 21 or above, and my current min is set to 16 (See screenshot below). I followed Android Studio's suggestion and override the version, thus created 2 separate activity_main.xml. This is what triggered the nullable behavior.

API restriction creating multiple xml files

When I deleted the v21 xml file, the nullable went away.

Now in my follow up questions for whoever reads this, why does asserting non-nullable still cause the app to crash? What is the correct way to handle this with multiple activity_main.xml versions?

Related